summaryrefslogtreecommitdiffstats
path: root/fdk-aac/libSBRdec/src/sbr_dec.cpp
diff options
context:
space:
mode:
authorMatthias P. Braendli <matthias.braendli@mpb.li>2019-11-11 11:38:02 +0100
committerMatthias P. Braendli <matthias.braendli@mpb.li>2019-11-11 11:38:02 +0100
commit0e5af65c467b2423a0b857ae3ad98c91acc1e190 (patch)
treed07f69550d8886271e44fe79c4dcfb299cafbd38 /fdk-aac/libSBRdec/src/sbr_dec.cpp
parentefe406d9724f959c8bc2a31802559ca6d41fd897 (diff)
downloadODR-AudioEnc-0e5af65c467b2423a0b857ae3ad98c91acc1e190.tar.gz
ODR-AudioEnc-0e5af65c467b2423a0b857ae3ad98c91acc1e190.tar.bz2
ODR-AudioEnc-0e5af65c467b2423a0b857ae3ad98c91acc1e190.zip
Include patched FDK-AAC in the repository
The initial idea was to get the DAB+ patch into upstream, but since that follows the android source releases, there is no place for a custom DAB+ patch there. So instead of having to maintain a patched fdk-aac that has to have the same .so version as the distribution package on which it is installed, we prefer having a separate fdk-aac-dab library to avoid collision. At that point, there's no reason to keep fdk-aac in a separate repository, as odr-audioenc is the only tool that needs DAB+ encoding support. Including it here simplifies installation, and makes it consistent with toolame-dab, also shipped in this repository. DAB+ decoding support (needed by ODR-SourceCompanion, dablin, etisnoop, welle.io and others) can be done using upstream FDK-AAC.
Diffstat (limited to 'fdk-aac/libSBRdec/src/sbr_dec.cpp')
-rw-r--r--fdk-aac/libSBRdec/src/sbr_dec.cpp1480
1 files changed, 1480 insertions, 0 deletions
diff --git a/fdk-aac/libSBRdec/src/sbr_dec.cpp b/fdk-aac/libSBRdec/src/sbr_dec.cpp
new file mode 100644
index 0000000..30611e7
--- /dev/null
+++ b/fdk-aac/libSBRdec/src/sbr_dec.cpp
@@ -0,0 +1,1480 @@
+/* -----------------------------------------------------------------------------
+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
+----------------------------------------------------------------------------- */
+
+/**************************** SBR decoder library ******************************
+
+ Author(s):
+
+ Description:
+
+*******************************************************************************/
+
+/*!
+ \file
+ \brief Sbr decoder
+ This module provides the actual decoder implementation. The SBR data (side
+ information) is already decoded. Only three functions are provided:
+
+ \li 1.) createSbrDec(): One time initialization
+ \li 2.) resetSbrDec(): Called by sbr_Apply() when the information contained in
+ an SBR_HEADER_ELEMENT requires a reset and recalculation of important SBR
+ structures. \li 3.) sbr_dec(): The actual decoder. Calls the different tools
+ such as filterbanks, lppTransposer(), and calculateSbrEnvelope() [the envelope
+ adjuster].
+
+ \sa sbr_dec(), \ref documentationOverview
+*/
+
+#include "sbr_dec.h"
+
+#include "sbr_ram.h"
+#include "env_extr.h"
+#include "env_calc.h"
+#include "scale.h"
+#include "FDK_matrixCalloc.h"
+#include "hbe.h"
+
+#include "genericStds.h"
+
+#include "sbrdec_drc.h"
+
+static void copyHarmonicSpectrum(int *xOverQmf, FIXP_DBL **qmfReal,
+ FIXP_DBL **qmfImag, int noCols, int overlap,
+ KEEP_STATES_SYNCED_MODE keepStatesSynced) {
+ int patchBands;
+ int patch, band, col, target, sourceBands, i;
+ int numPatches = 0;
+ int slotOffset = 0;
+
+ FIXP_DBL **ppqmfReal = qmfReal + overlap;
+ FIXP_DBL **ppqmfImag = qmfImag + overlap;
+
+ if (keepStatesSynced == KEEP_STATES_SYNCED_NORMAL) {
+ slotOffset = noCols - overlap - LPC_ORDER;
+ }
+
+ if (keepStatesSynced == KEEP_STATES_SYNCED_OUTDIFF) {
+ ppqmfReal = qmfReal;
+ ppqmfImag = qmfImag;
+ }
+
+ for (i = 1; i < MAX_NUM_PATCHES; i++) {
+ if (xOverQmf[i] != 0) {
+ numPatches++;
+ }
+ }
+
+ for (patch = (MAX_STRETCH_HBE - 1); patch < numPatches; patch++) {
+ patchBands = xOverQmf[patch + 1] - xOverQmf[patch];
+ target = xOverQmf[patch];
+ sourceBands = xOverQmf[MAX_STRETCH_HBE - 1] - xOverQmf[MAX_STRETCH_HBE - 2];
+
+ while (patchBands > 0) {
+ int numBands = sourceBands;
+ int startBand = xOverQmf[MAX_STRETCH_HBE - 1] - 1;
+ if (target + numBands >= xOverQmf[patch + 1]) {
+ numBands = xOverQmf[patch + 1] - target;
+ }
+ if ((((target + numBands - 1) % 2) +
+ ((xOverQmf[MAX_STRETCH_HBE - 1] - 1) % 2)) %
+ 2) {
+ if (numBands == sourceBands) {
+ numBands--;
+ } else {
+ startBand--;
+ }
+ }
+ if (keepStatesSynced == KEEP_STATES_SYNCED_OUTDIFF) {
+ for (col = slotOffset; col < overlap + LPC_ORDER; col++) {
+ i = 0;
+ for (band = numBands; band > 0; band--) {
+ if ((target + band - 1 < 64) &&
+ (target + band - 1 < xOverQmf[patch + 1])) {
+ ppqmfReal[col][target + band - 1] = ppqmfReal[col][startBand - i];
+ ppqmfImag[col][target + band - 1] = ppqmfImag[col][startBand - i];
+ i++;
+ }
+ }
+ }
+ } else {
+ for (col = slotOffset; col < noCols; col++) {
+ i = 0;
+ for (band = numBands; band > 0; band--) {
+ if ((target + band - 1 < 64) &&
+ (target + band - 1 < xOverQmf[patch + 1])) {
+ ppqmfReal[col][target + band - 1] = ppqmfReal[col][startBand - i];
+ ppqmfImag[col][target + band - 1] = ppqmfImag[col][startBand - i];
+ i++;
+ }
+ }
+ }
+ }
+ target += numBands;
+ patchBands -= numBands;
+ }
+ }
+}
+
+/*!
+ \brief SBR decoder core function for one channel
+
+ \image html BufferMgmtDetailed-1632.png
+
+ Besides the filter states of the QMF filter bank and the LPC-states of
+ the LPP-Transposer, processing is mainly based on four buffers:
+ #timeIn, #timeOut, #WorkBuffer2 and #OverlapBuffer. The #WorkBuffer2
+ is reused for all channels and might be used by the core decoder, a
+ static overlap buffer is required for each channel. Due to in-place
+ processing, #timeIn and #timeOut point to identical locations.
+
+ The spectral data is organized in so-called slots. Each slot
+ contains 64 bands of complex data. The number of slots per frame
+ depends on the frame size. For mp3PRO, there are 18 slots per frame
+ and 6 slots per #OverlapBuffer. It is not necessary to have the slots
+ in located consecutive address ranges.
+
+ To optimize memory usage and to minimize the number of memory
+ accesses, the memory management is organized as follows (slot numbers
+ based on mp3PRO):
+
+ 1.) Input time domain signal is located in #timeIn. The last slots
+ (0..5) of the spectral data of the previous frame are located in the
+ #OverlapBuffer. In addition, #frameData of the current frame resides
+ in the upper part of #timeIn.
+
+ 2.) During the cplxAnalysisQmfFiltering(), 32 samples from #timeIn are
+ transformed into a slot of up to 32 complex spectral low band values at a
+ time. The first spectral slot -- nr. 6 -- is written at slot number
+ zero of #WorkBuffer2. #WorkBuffer2 will be completely filled with
+ spectral data.
+
+ 3.) LPP-Transposition in lppTransposer() is processed on 24 slots. During the
+ transposition, the high band part of the spectral data is replicated
+ based on the low band data.
+
+ Envelope Adjustment is processed on the high band part of the spectral
+ data only by calculateSbrEnvelope().
+
+ 4.) The cplxSynthesisQmfFiltering() creates 64 time domain samples out
+ of a slot of 64 complex spectral values at a time. The first 6 slots
+ in #timeOut are filled from the results of spectral slots 0..5 in the
+ #OverlapBuffer. The consecutive slots in timeOut are now filled with
+ the results of spectral slots 6..17.
+
+ 5.) The preprocessed slots 18..23 have to be stored in the
+ #OverlapBuffer.
+
+*/
+
+void sbr_dec(
+ HANDLE_SBR_DEC hSbrDec, /*!< handle to Decoder channel */
+ INT_PCM *timeIn, /*!< pointer to input time signal */
+ INT_PCM *timeOut, /*!< pointer to output time signal */
+ HANDLE_SBR_DEC hSbrDecRight, /*!< handle to Decoder channel right */
+ INT_PCM *timeOutRight, /*!< pointer to output time signal */
+ const int strideOut, /*!< Time data traversal strideOut */
+ HANDLE_SBR_HEADER_DATA hHeaderData, /*!< Static control data */
+ HANDLE_SBR_FRAME_DATA hFrameData, /*!< Control data of current frame */
+ HANDLE_SBR_PREV_FRAME_DATA
+ hPrevFrameData, /*!< Some control data of last frame */
+ const int applyProcessing, /*!< Flag for SBR operation */
+ HANDLE_PS_DEC h_ps_d, const UINT flags, const int codecFrameSize) {
+ int i, slot, reserve;
+ int saveLbScale;
+ int lastSlotOffs;
+ FIXP_DBL maxVal;
+
+ /* temporary pointer / variable for QMF;
+ required as we want to use temporary buffer
+ creating one frame delay for HBE in LP mode */
+ INT_PCM *pTimeInQmf = timeIn;
+
+ /* Number of QMF timeslots in the overlap buffer: */
+ int ov_len = hSbrDec->LppTrans.pSettings->overlap;
+
+ /* Number of QMF slots per frame */
+ int noCols = hHeaderData->numberTimeSlots * hHeaderData->timeStep;
+
+ /* create pointer array for data to use for HBE and legacy sbr */
+ FIXP_DBL *pLowBandReal[(3 * 4) + 2 * ((1024) / (32) * (4) / 2)];
+ FIXP_DBL *pLowBandImag[(3 * 4) + 2 * ((1024) / (32) * (4) / 2)];
+
+ /* set pReal to where QMF analysis writes in case of legacy SBR */
+ FIXP_DBL **pReal = pLowBandReal + ov_len;
+ FIXP_DBL **pImag = pLowBandImag + ov_len;
+
+ /* map QMF buffer to pointer array (Overlap + Frame)*/
+ for (i = 0; i < noCols + ov_len; i++) {
+ pLowBandReal[i] = hSbrDec->qmfDomainInCh->hQmfSlotsReal[i];
+ pLowBandImag[i] = hSbrDec->qmfDomainInCh->hQmfSlotsImag[i];
+ }
+
+ if ((flags & SBRDEC_USAC_HARMONICSBR)) {
+ /* in case of harmonic SBR and no HBE_LP map additional buffer for
+ one more frame to pointer arry */
+ for (i = 0; i < noCols; i++) {
+ pLowBandReal[i + noCols + ov_len] = hSbrDec->hQmfHBESlotsReal[i];
+ pLowBandImag[i + noCols + ov_len] = hSbrDec->hQmfHBESlotsImag[i];
+ }
+
+ /* shift scale values according to buffer */
+ hSbrDec->scale_ov = hSbrDec->scale_lb;
+ hSbrDec->scale_lb = hSbrDec->scale_hbe;
+
+ /* set pReal to where QMF analysis writes in case of HBE */
+ pReal += noCols;
+ pImag += noCols;
+ if (flags & SBRDEC_SKIP_QMF_ANA) {
+ /* stereoCfgIndex3 with HBE */
+ FDK_QmfDomain_QmfData2HBE(hSbrDec->qmfDomainInCh,
+ hSbrDec->hQmfHBESlotsReal,
+ hSbrDec->hQmfHBESlotsImag);
+ } else {
+ /* We have to move old hbe frame data to lb area of buffer */
+ for (i = 0; i < noCols; i++) {
+ FDKmemcpy(pLowBandReal[ov_len + i], hSbrDec->hQmfHBESlotsReal[i],
+ hHeaderData->numberOfAnalysisBands * sizeof(FIXP_DBL));
+ FDKmemcpy(pLowBandImag[ov_len + i], hSbrDec->hQmfHBESlotsImag[i],
+ hHeaderData->numberOfAnalysisBands * sizeof(FIXP_DBL));
+ }
+ }
+ }
+
+ /*
+ low band codec signal subband filtering
+ */
+
+ if (flags & SBRDEC_SKIP_QMF_ANA) {
+ if (!(flags & SBRDEC_USAC_HARMONICSBR)) /* stereoCfgIndex3 w/o HBE */
+ FDK_QmfDomain_WorkBuffer2ProcChannel(hSbrDec->qmfDomainInCh);
+ } else {
+ C_AALLOC_SCRATCH_START(qmfTemp, FIXP_DBL, 2 * (64));
+ qmfAnalysisFiltering(&hSbrDec->qmfDomainInCh->fb, pReal, pImag,
+ &hSbrDec->qmfDomainInCh->scaling, pTimeInQmf, 0, 1,
+ qmfTemp);
+
+ C_AALLOC_SCRATCH_END(qmfTemp, FIXP_DBL, 2 * (64));
+ }
+
+ /*
+ Clear upper half of spectrum
+ */
+ if (!((flags & SBRDEC_USAC_HARMONICSBR) &&
+ (hFrameData->sbrPatchingMode == 0))) {
+ int nAnalysisBands = hHeaderData->numberOfAnalysisBands;
+
+ if (!(flags & SBRDEC_LOW_POWER)) {
+ for (slot = ov_len; slot < noCols + ov_len; slot++) {
+ FDKmemclear(&pLowBandReal[slot][nAnalysisBands],
+ ((64) - nAnalysisBands) * sizeof(FIXP_DBL));
+ FDKmemclear(&pLowBandImag[slot][nAnalysisBands],
+ ((64) - nAnalysisBands) * sizeof(FIXP_DBL));
+ }
+ } else {
+ for (slot = ov_len; slot < noCols + ov_len; slot++) {
+ FDKmemclear(&pLowBandReal[slot][nAnalysisBands],
+ ((64) - nAnalysisBands) * sizeof(FIXP_DBL));
+ }
+ }
+ }
+
+ /*
+ Shift spectral data left to gain accuracy in transposer and adjustor
+ */
+ /* Range was increased from lsb to no_channels because in some cases (e.g.
+ USAC conf eSbr_4_Pvc.mp4 and some HBE cases) it could be observed that the
+ signal between lsb and no_channels is used for the patching process.
+ */
+ maxVal = maxSubbandSample(pReal, (flags & SBRDEC_LOW_POWER) ? NULL : pImag, 0,
+ hSbrDec->qmfDomainInCh->fb.no_channels, 0, noCols);
+
+ reserve = fixMax(0, CntLeadingZeros(maxVal) - 1);
+ reserve = fixMin(reserve,
+ DFRACT_BITS - 1 - hSbrDec->qmfDomainInCh->scaling.lb_scale);
+
+ /* If all data is zero, lb_scale could become too large */
+ rescaleSubbandSamples(pReal, (flags & SBRDEC_LOW_POWER) ? NULL : pImag, 0,
+ hSbrDec->qmfDomainInCh->fb.no_channels, 0, noCols,
+ reserve);
+
+ hSbrDec->qmfDomainInCh->scaling.lb_scale += reserve;
+
+ if ((flags & SBRDEC_USAC_HARMONICSBR)) {
+ /* actually this is our hbe_scale */
+ hSbrDec->scale_hbe = hSbrDec->qmfDomainInCh->scaling.lb_scale;
+ /* the real lb_scale is stored in scale_lb from sbr */
+ hSbrDec->qmfDomainInCh->scaling.lb_scale = hSbrDec->scale_lb;
+ }
+ /*
+ save low band scale, wavecoding or parametric stereo may modify it
+ */
+ saveLbScale = hSbrDec->qmfDomainInCh->scaling.lb_scale;
+
+ if (applyProcessing) {
+ UCHAR *borders = hFrameData->frameInfo.borders;
+ lastSlotOffs = borders[hFrameData->frameInfo.nEnvelopes] -
+ hHeaderData->numberTimeSlots;
+
+ FIXP_DBL degreeAlias[(64)];
+ PVC_DYNAMIC_DATA pvcDynamicData;
+ pvcInitFrame(
+ &hSbrDec->PvcStaticData, &pvcDynamicData,
+ (hHeaderData->frameErrorFlag ? 0 : hHeaderData->bs_info.pvc_mode),
+ hFrameData->ns, hHeaderData->timeStep,
+ hHeaderData->freqBandData.lowSubband,
+ hFrameData->frameInfo.pvcBorders[0], hFrameData->pvcID);
+
+ if (!hHeaderData->frameErrorFlag && (hHeaderData->bs_info.pvc_mode > 0)) {
+ pvcDecodeFrame(&hSbrDec->PvcStaticData, &pvcDynamicData, pLowBandReal,
+ pLowBandImag, ov_len,
+ SCALE2EXP(hSbrDec->qmfDomainInCh->scaling.ov_lb_scale),
+ SCALE2EXP(hSbrDec->qmfDomainInCh->scaling.lb_scale));
+ }
+ pvcEndFrame(&hSbrDec->PvcStaticData, &pvcDynamicData);
+
+ /* The transposer will override most values in degreeAlias[].
+ The array needs to be cleared at least from lowSubband to highSubband
+ before. */
+ if (flags & SBRDEC_LOW_POWER)
+ FDKmemclear(&degreeAlias[hHeaderData->freqBandData.lowSubband],
+ (hHeaderData->freqBandData.highSubband -
+ hHeaderData->freqBandData.lowSubband) *
+ sizeof(FIXP_DBL));
+
+ /*
+ Inverse filtering of lowband and transposition into the SBR-frequency
+ range
+ */
+
+ {
+ KEEP_STATES_SYNCED_MODE keepStatesSyncedMode =
+ ((flags & SBRDEC_USAC_HARMONICSBR) &&
+ (hFrameData->sbrPatchingMode != 0))
+ ? KEEP_STATES_SYNCED_NORMAL
+ : KEEP_STATES_SYNCED_OFF;
+
+ if (flags & SBRDEC_USAC_HARMONICSBR) {
+ if (flags & SBRDEC_QUAD_RATE) {
+ pReal -= 32;
+ pImag -= 32;
+ }
+
+ if ((hSbrDec->savedStates == 0) && (hFrameData->sbrPatchingMode == 1)) {
+ /* copy saved states from previous frame to legacy SBR lpc filterstate
+ * buffer */
+ for (i = 0; i < LPC_ORDER + ov_len; i++) {
+ FDKmemcpy(
+ hSbrDec->LppTrans.lpcFilterStatesRealLegSBR[i],
+ hSbrDec->codecQMFBufferReal[noCols - LPC_ORDER - ov_len + i],
+ hSbrDec->hHBE->noChannels * sizeof(FIXP_DBL));
+ FDKmemcpy(
+ hSbrDec->LppTrans.lpcFilterStatesImagLegSBR[i],
+ hSbrDec->codecQMFBufferImag[noCols - LPC_ORDER - ov_len + i],
+ hSbrDec->hHBE->noChannels * sizeof(FIXP_DBL));
+ }
+ }
+
+ /* saving unmodified QMF states in case we are switching from legacy SBR
+ * to HBE */
+ for (i = 0; i < hSbrDec->hHBE->noCols; i++) {
+ FDKmemcpy(hSbrDec->codecQMFBufferReal[i], pLowBandReal[ov_len + i],
+ hSbrDec->hHBE->noChannels * sizeof(FIXP_DBL));
+ FDKmemcpy(hSbrDec->codecQMFBufferImag[i], pLowBandImag[ov_len + i],
+ hSbrDec->hHBE->noChannels * sizeof(FIXP_DBL));
+ }
+
+ QmfTransposerApply(
+ hSbrDec->hHBE, pReal, pImag, noCols, pLowBandReal, pLowBandImag,
+ hSbrDec->LppTrans.lpcFilterStatesRealHBE,
+ hSbrDec->LppTrans.lpcFilterStatesImagHBE,
+ hFrameData->sbrPitchInBins, hSbrDec->scale_lb, hSbrDec->scale_hbe,
+ &hSbrDec->qmfDomainInCh->scaling.hb_scale, hHeaderData->timeStep,
+ borders[0], ov_len, keepStatesSyncedMode);
+
+ if (flags & SBRDEC_QUAD_RATE) {
+ int *xOverQmf = GetxOverBandQmfTransposer(hSbrDec->hHBE);
+
+ copyHarmonicSpectrum(xOverQmf, pLowBandReal, pLowBandImag, noCols,
+ ov_len, keepStatesSyncedMode);
+ }
+ }
+ }
+
+ if ((flags & SBRDEC_USAC_HARMONICSBR) &&
+ (hFrameData->sbrPatchingMode == 0)) {
+ hSbrDec->prev_frame_lSbr = 0;
+ hSbrDec->prev_frame_hbeSbr = 1;
+
+ lppTransposerHBE(
+ &hSbrDec->LppTrans, hSbrDec->hHBE, &hSbrDec->qmfDomainInCh->scaling,
+ pLowBandReal, pLowBandImag, hHeaderData->timeStep, borders[0],
+ lastSlotOffs, hHeaderData->freqBandData.nInvfBands,
+ hFrameData->sbr_invf_mode, hPrevFrameData->sbr_invf_mode);
+
+ } else {
+ if (flags & SBRDEC_USAC_HARMONICSBR) {
+ for (i = 0; i < LPC_ORDER + hSbrDec->LppTrans.pSettings->overlap; i++) {
+ /*
+ Store the unmodified qmf Slots values for upper part of spectrum
+ (required for LPC filtering) required if next frame is a HBE frame
+ */
+ FDKmemcpy(hSbrDec->LppTrans.lpcFilterStatesRealHBE[i],
+ hSbrDec->qmfDomainInCh
+ ->hQmfSlotsReal[hSbrDec->hHBE->noCols - LPC_ORDER + i],
+ (64) * sizeof(FIXP_DBL));
+ FDKmemcpy(hSbrDec->LppTrans.lpcFilterStatesImagHBE[i],
+ hSbrDec->qmfDomainInCh
+ ->hQmfSlotsImag[hSbrDec->hHBE->noCols - LPC_ORDER + i],
+ (64) * sizeof(FIXP_DBL));
+ }
+ }
+ {
+ hSbrDec->prev_frame_lSbr = 1;
+ hSbrDec->prev_frame_hbeSbr = 0;
+ }
+
+ lppTransposer(
+ &hSbrDec->LppTrans, &hSbrDec->qmfDomainInCh->scaling, pLowBandReal,
+ degreeAlias, // only used if useLP = 1
+ pLowBandImag, flags & SBRDEC_LOW_POWER,
+ hHeaderData->bs_info.sbr_preprocessing,
+ hHeaderData->freqBandData.v_k_master[0], hHeaderData->timeStep,
+ borders[0], lastSlotOffs, hHeaderData->freqBandData.nInvfBands,
+ hFrameData->sbr_invf_mode, hPrevFrameData->sbr_invf_mode);
+ }
+
+ /*
+ Adjust envelope of current frame.
+ */
+
+ if ((hFrameData->sbrPatchingMode !=
+ hSbrDec->SbrCalculateEnvelope.sbrPatchingMode)) {
+ ResetLimiterBands(hHeaderData->freqBandData.limiterBandTable,
+ &hHeaderData->freqBandData.noLimiterBands,
+ hHeaderData->freqBandData.freqBandTable[0],
+ hHeaderData->freqBandData.nSfb[0],
+ hSbrDec->LppTrans.pSettings->patchParam,
+ hSbrDec->LppTrans.pSettings->noOfPatches,
+ hHeaderData->bs_data.limiterBands,
+ hFrameData->sbrPatchingMode,
+ (flags & SBRDEC_USAC_HARMONICSBR) &&
+ (hFrameData->sbrPatchingMode == 0)
+ ? GetxOverBandQmfTransposer(hSbrDec->hHBE)
+ : NULL,
+ Get41SbrQmfTransposer(hSbrDec->hHBE));
+
+ hSbrDec->SbrCalculateEnvelope.sbrPatchingMode =
+ hFrameData->sbrPatchingMode;
+ }
+
+ calculateSbrEnvelope(
+ &hSbrDec->qmfDomainInCh->scaling, &hSbrDec->SbrCalculateEnvelope,
+ hHeaderData, hFrameData, &pvcDynamicData, pLowBandReal, pLowBandImag,
+ flags & SBRDEC_LOW_POWER,
+
+ degreeAlias, flags,
+ (hHeaderData->frameErrorFlag || hPrevFrameData->frameErrorFlag));
+
+#if (SBRDEC_MAX_HB_FADE_FRAMES > 0)
+ /* Avoid hard onsets of high band */
+ if (hHeaderData->frameErrorFlag) {
+ if (hSbrDec->highBandFadeCnt < SBRDEC_MAX_HB_FADE_FRAMES) {
+ hSbrDec->highBandFadeCnt += 1;
+ }
+ } else {
+ if (hSbrDec->highBandFadeCnt >
+ 0) { /* Manipulate high band scale factor to get a smooth fade-in */
+ hSbrDec->qmfDomainInCh->scaling.hb_scale += hSbrDec->highBandFadeCnt;
+ hSbrDec->qmfDomainInCh->scaling.hb_scale =
+ fMin(hSbrDec->qmfDomainInCh->scaling.hb_scale, DFRACT_BITS - 1);
+ hSbrDec->highBandFadeCnt -= 1;
+ }
+ }
+
+#endif
+ /*
+ Update hPrevFrameData (to be used in the next frame)
+ */
+ for (i = 0; i < hHeaderData->freqBandData.nInvfBands; i++) {
+ hPrevFrameData->sbr_invf_mode[i] = hFrameData->sbr_invf_mode[i];
+ }
+ hPrevFrameData->coupling = hFrameData->coupling;
+ hPrevFrameData->stopPos = borders[hFrameData->frameInfo.nEnvelopes];
+ hPrevFrameData->ampRes = hFrameData->ampResolutionCurrentFrame;
+ hPrevFrameData->prevSbrPitchInBins = hFrameData->sbrPitchInBins;
+ /* could be done in extractFrameInfo_pvc() but hPrevFrameData is not
+ * available there */
+ FDKmemcpy(&hPrevFrameData->prevFrameInfo, &hFrameData->frameInfo,
+ sizeof(FRAME_INFO));
+ } else {
+ /* rescale from lsb to nAnalysisBands in order to compensate scaling with
+ * hb_scale in this area, done by synthesisFiltering*/
+ int rescale;
+ int lsb;
+ int length;
+
+ /* Reset hb_scale if no highband is present, because hb_scale is considered
+ * in the QMF-synthesis */
+ hSbrDec->qmfDomainInCh->scaling.hb_scale = saveLbScale;
+
+ rescale = hSbrDec->qmfDomainInCh->scaling.hb_scale -
+ hSbrDec->qmfDomainInCh->scaling.ov_lb_scale;
+ lsb = hSbrDec->qmfDomainOutCh->fb.lsb;
+ length = (hSbrDec->qmfDomainInCh->fb.no_channels - lsb);
+
+ if ((rescale < 0) && (length > 0)) {
+ if (!(flags & SBRDEC_LOW_POWER)) {
+ for (i = 0; i < ov_len; i++) {
+ scaleValues(&pLowBandReal[i][lsb], length, rescale);
+ scaleValues(&pLowBandImag[i][lsb], length, rescale);
+ }
+ } else {
+ for (i = 0; i < ov_len; i++) {
+ scaleValues(&pLowBandReal[i][lsb], length, rescale);
+ }
+ }
+ }
+ }
+
+ if (!(flags & SBRDEC_USAC_HARMONICSBR)) {
+ int length = hSbrDec->qmfDomainInCh->fb.lsb;
+ if (flags & SBRDEC_SYNTAX_USAC) {
+ length = hSbrDec->qmfDomainInCh->fb.no_channels;
+ }
+
+ /* in case of legacy sbr saving of filter states here */
+ for (i = 0; i < LPC_ORDER + ov_len; i++) {
+ /*
+ Store the unmodified qmf Slots values (required for LPC filtering)
+ */
+ if (!(flags & SBRDEC_LOW_POWER)) {
+ FDKmemcpy(hSbrDec->LppTrans.lpcFilterStatesRealLegSBR[i],
+ pLowBandReal[noCols - LPC_ORDER + i],
+ length * sizeof(FIXP_DBL));
+ FDKmemcpy(hSbrDec->LppTrans.lpcFilterStatesImagLegSBR[i],
+ pLowBandImag[noCols - LPC_ORDER + i],
+ length * sizeof(FIXP_DBL));
+ } else
+ FDKmemcpy(hSbrDec->LppTrans.lpcFilterStatesRealLegSBR[i],
+ pLowBandReal[noCols - LPC_ORDER + i],
+ length * sizeof(FIXP_DBL));
+ }
+ }
+
+ /*
+ Synthesis subband filtering.
+ */
+
+ if (!(flags & SBRDEC_PS_DECODED)) {
+ if (!(flags & SBRDEC_SKIP_QMF_SYN)) {
+ int outScalefactor = 0;
+
+ if (h_ps_d != NULL) {
+ h_ps_d->procFrameBased = 1; /* we here do frame based processing */
+ }
+
+ sbrDecoder_drcApply(&hSbrDec->sbrDrcChannel, pLowBandReal,
+ (flags & SBRDEC_LOW_POWER) ? NULL : pLowBandImag,
+ hSbrDec->qmfDomainOutCh->fb.no_col, &outScalefactor);
+
+ qmfChangeOutScalefactor(&hSbrDec->qmfDomainOutCh->fb, outScalefactor);
+
+ {
+ HANDLE_FREQ_BAND_DATA hFreq = &hHeaderData->freqBandData;
+ int save_usb = hSbrDec->qmfDomainOutCh->fb.usb;
+
+#if (QMF_MAX_SYNTHESIS_BANDS <= 64)
+ C_AALLOC_SCRATCH_START(qmfTemp, FIXP_DBL, 2 * QMF_MAX_SYNTHESIS_BANDS);
+#else
+ C_AALLOC_STACK_START(qmfTemp, FIXP_DBL, 2 * QMF_MAX_SYNTHESIS_BANDS);
+#endif
+ if (hSbrDec->qmfDomainOutCh->fb.usb < hFreq->ov_highSubband) {
+ /* we need to patch usb for this frame as overlap may contain higher
+ frequency range if headerchange occured; fb. usb is always limited
+ to maximum fb.no_channels; In case of wrongly decoded headers it
+ might be that ov_highSubband is higher than the number of synthesis
+ channels (fb.no_channels), which is forbidden, therefore we need to
+ limit ov_highSubband with fMin function to avoid not allowed usb in
+ synthesis filterbank. */
+ hSbrDec->qmfDomainOutCh->fb.usb =
+ fMin((UINT)hFreq->ov_highSubband,
+ (UINT)hSbrDec->qmfDomainOutCh->fb.no_channels);
+ }
+ {
+ qmfSynthesisFiltering(
+ &hSbrDec->qmfDomainOutCh->fb, pLowBandReal,
+ (flags & SBRDEC_LOW_POWER) ? NULL : pLowBandImag,
+ &hSbrDec->qmfDomainInCh->scaling,
+ hSbrDec->LppTrans.pSettings->overlap, timeOut, strideOut,
+ qmfTemp);
+ }
+ /* restore saved value */
+ hSbrDec->qmfDomainOutCh->fb.usb = save_usb;
+ hFreq->ov_highSubband = save_usb;
+#if (QMF_MAX_SYNTHESIS_BANDS <= 64)
+ C_AALLOC_SCRATCH_END(qmfTemp, FIXP_DBL, 2 * QMF_MAX_SYNTHESIS_BANDS);
+#else
+ C_AALLOC_STACK_END(qmfTemp, FIXP_DBL, 2 * QMF_MAX_SYNTHESIS_BANDS);
+#endif
+ }
+ }
+
+ } else { /* (flags & SBRDEC_PS_DECODED) */
+ INT sdiff;
+ INT scaleFactorHighBand, scaleFactorLowBand_ov, scaleFactorLowBand_no_ov;
+
+ HANDLE_QMF_FILTER_BANK synQmf = &hSbrDec->qmfDomainOutCh->fb;
+ HANDLE_QMF_FILTER_BANK synQmfRight = &hSbrDecRight->qmfDomainOutCh->fb;
+
+ /* adapt scaling */
+ sdiff = hSbrDec->qmfDomainInCh->scaling.lb_scale -
+ reserve; /* Scaling difference */
+ scaleFactorHighBand = sdiff - hSbrDec->qmfDomainInCh->scaling.hb_scale;
+ scaleFactorLowBand_ov = sdiff - hSbrDec->qmfDomainInCh->scaling.ov_lb_scale;
+ scaleFactorLowBand_no_ov = sdiff - hSbrDec->qmfDomainInCh->scaling.lb_scale;
+
+ /* Scale of low band overlapping QMF data */
+ scaleFactorLowBand_ov =
+ fMin(DFRACT_BITS - 1, fMax(-(DFRACT_BITS - 1), scaleFactorLowBand_ov));
+ /* Scale of low band current QMF data */
+ scaleFactorLowBand_no_ov = fMin(
+ DFRACT_BITS - 1, fMax(-(DFRACT_BITS - 1), scaleFactorLowBand_no_ov));
+ /* Scale of current high band */
+ scaleFactorHighBand =
+ fMin(DFRACT_BITS - 1, fMax(-(DFRACT_BITS - 1), scaleFactorHighBand));
+
+ if (h_ps_d->procFrameBased == 1) /* If we have switched from frame to slot
+ based processing copy filter states */
+ { /* procFrameBased will be unset later */
+ /* copy filter states from left to right */
+ /* was ((640)-(64))*sizeof(FIXP_QSS)
+ flexible amount of synthesis bands needed for QMF based resampling
+ */
+ FDK_ASSERT(hSbrDec->qmfDomainInCh->pGlobalConf->nBandsSynthesis <=
+ QMF_MAX_SYNTHESIS_BANDS);
+ FDKmemcpy(synQmfRight->FilterStates, synQmf->FilterStates,
+ 9 * hSbrDec->qmfDomainInCh->pGlobalConf->nBandsSynthesis *
+ sizeof(FIXP_QSS));
+ }
+
+ /* Feed delaylines when parametric stereo is switched on. */
+ PreparePsProcessing(h_ps_d, pLowBandReal, pLowBandImag,
+ scaleFactorLowBand_ov);
+
+ /* use the same synthese qmf values for left and right channel */
+ synQmfRight->no_col = synQmf->no_col;
+ synQmfRight->lsb = synQmf->lsb;
+ synQmfRight->usb = synQmf->usb;
+
+ int env = 0;
+
+ {
+#if (QMF_MAX_SYNTHESIS_BANDS <= 64)
+ C_AALLOC_SCRATCH_START(pWorkBuffer, FIXP_DBL,
+ 2 * QMF_MAX_SYNTHESIS_BANDS);
+#else
+ C_AALLOC_STACK_START(pWorkBuffer, FIXP_DBL, 2 * QMF_MAX_SYNTHESIS_BANDS);
+#endif
+
+ int maxShift = 0;
+
+ if (hSbrDec->sbrDrcChannel.enable != 0) {
+ if (hSbrDec->sbrDrcChannel.prevFact_exp > maxShift) {
+ maxShift = hSbrDec->sbrDrcChannel.prevFact_exp;
+ }
+ if (hSbrDec->sbrDrcChannel.currFact_exp > maxShift) {
+ maxShift = hSbrDec->sbrDrcChannel.currFact_exp;
+ }
+ if (hSbrDec->sbrDrcChannel.nextFact_exp > maxShift) {
+ maxShift = hSbrDec->sbrDrcChannel.nextFact_exp;
+ }
+ }
+
+ /* copy DRC data to right channel (with PS both channels use the same DRC
+ * gains) */
+ FDKmemcpy(&hSbrDecRight->sbrDrcChannel, &hSbrDec->sbrDrcChannel,
+ sizeof(SBRDEC_DRC_CHANNEL));
+
+ for (i = 0; i < synQmf->no_col; i++) { /* ----- no_col loop ----- */
+
+ INT outScalefactorR, outScalefactorL;
+
+ /* qmf timeslot of right channel */
+ FIXP_DBL *rQmfReal = pWorkBuffer;
+ FIXP_DBL *rQmfImag = pWorkBuffer + synQmf->no_channels;
+
+ {
+ if (i ==
+ h_ps_d->bsData[h_ps_d->processSlot].mpeg.aEnvStartStop[env]) {
+ initSlotBasedRotation(h_ps_d, env,
+ hHeaderData->freqBandData.highSubband);
+ env++;
+ }
+
+ ApplyPsSlot(
+ h_ps_d, /* parametric stereo decoder handle */
+ (pLowBandReal + i), /* one timeslot of left/mono channel */
+ (pLowBandImag + i), /* one timeslot of left/mono channel */
+ rQmfReal, /* one timeslot or right channel */
+ rQmfImag, /* one timeslot or right channel */
+ scaleFactorLowBand_no_ov,
+ (i < hSbrDec->LppTrans.pSettings->overlap)
+ ? scaleFactorLowBand_ov
+ : scaleFactorLowBand_no_ov,
+ scaleFactorHighBand, synQmf->lsb, synQmf->usb);
+
+ outScalefactorL = outScalefactorR = 1; /* psDiffScale! (MPEG-PS) */
+ }
+
+ sbrDecoder_drcApplySlot(/* right channel */
+ &hSbrDecRight->sbrDrcChannel, rQmfReal,
+ rQmfImag, i, synQmfRight->no_col, maxShift);
+
+ outScalefactorR += maxShift;
+
+ sbrDecoder_drcApplySlot(/* left channel */
+ &hSbrDec->sbrDrcChannel, *(pLowBandReal + i),
+ *(pLowBandImag + i), i, synQmf->no_col,
+ maxShift);
+
+ outScalefactorL += maxShift;
+
+ if (!(flags & SBRDEC_SKIP_QMF_SYN)) {
+ qmfSynthesisFilteringSlot(
+ synQmfRight, rQmfReal, /* QMF real buffer */
+ rQmfImag, /* QMF imag buffer */
+ outScalefactorL, outScalefactorL,
+ timeOutRight + (i * synQmf->no_channels * strideOut), strideOut,
+ pWorkBuffer);
+
+ qmfSynthesisFilteringSlot(
+ synQmf, *(pLowBandReal + i), /* QMF real buffer */
+ *(pLowBandImag + i), /* QMF imag buffer */
+ outScalefactorR, outScalefactorR,
+ timeOut + (i * synQmf->no_channels * strideOut), strideOut,
+ pWorkBuffer);
+ }
+ } /* no_col loop i */
+#if (QMF_MAX_SYNTHESIS_BANDS <= 64)
+ C_AALLOC_SCRATCH_END(pWorkBuffer, FIXP_DBL, 2 * QMF_MAX_SYNTHESIS_BANDS);
+#else
+ C_AALLOC_STACK_END(pWorkBuffer, FIXP_DBL, 2 * QMF_MAX_SYNTHESIS_BANDS);
+#endif
+ }
+ }
+
+ sbrDecoder_drcUpdateChannel(&hSbrDec->sbrDrcChannel);
+
+ /*
+ Update overlap buffer
+ Even bands above usb are copied to avoid outdated spectral data in case
+ the stop frequency raises.
+ */
+
+ if (!(flags & SBRDEC_SKIP_QMF_SYN)) {
+ {
+ FDK_QmfDomain_SaveOverlap(hSbrDec->qmfDomainInCh, 0);
+ FDK_ASSERT(hSbrDec->qmfDomainInCh->scaling.ov_lb_scale == saveLbScale);
+ }
+ }
+
+ hSbrDec->savedStates = 0;
+
+ /* Save current frame status */
+ hPrevFrameData->frameErrorFlag = hHeaderData->frameErrorFlag;
+ hSbrDec->applySbrProc_old = applyProcessing;
+
+} /* sbr_dec() */
+
+/*!
+ \brief Creates sbr decoder structure
+ \return errorCode, 0 if successful
+*/
+SBR_ERROR
+createSbrDec(SBR_CHANNEL *hSbrChannel,
+ HANDLE_SBR_HEADER_DATA hHeaderData, /*!< Static control data */
+ TRANSPOSER_SETTINGS *pSettings,
+ const int downsampleFac, /*!< Downsampling factor */
+ const UINT qmfFlags, /*!< flags -> 1: HQ/LP selector, 2: CLDFB */
+ const UINT flags, const int overlap,
+ int chan, /*!< Channel for which to assign buffers etc. */
+ int codecFrameSize)
+
+{
+ SBR_ERROR err = SBRDEC_OK;
+ int timeSlots =
+ hHeaderData->numberTimeSlots; /* Number of SBR slots per frame */
+ int noCols =
+ timeSlots * hHeaderData->timeStep; /* Number of QMF slots per frame */
+ HANDLE_SBR_DEC hs = &(hSbrChannel->SbrDec);
+
+#if (SBRDEC_MAX_HB_FADE_FRAMES > 0)
+ hs->highBandFadeCnt = SBRDEC_MAX_HB_FADE_FRAMES;
+
+#endif
+ hs->scale_hbe = 15;
+ hs->scale_lb = 15;
+ hs->scale_ov = 15;
+
+ hs->prev_frame_lSbr = 0;
+ hs->prev_frame_hbeSbr = 0;
+
+ hs->codecFrameSize = codecFrameSize;
+
+ /*
+ create envelope calculator
+ */
+ err = createSbrEnvelopeCalc(&hs->SbrCalculateEnvelope, hHeaderData, chan,
+ flags);
+ if (err != SBRDEC_OK) {
+ return err;
+ }
+
+ initSbrPrevFrameData(&hSbrChannel->prevFrameData, timeSlots);
+
+ /*
+ create transposer
+ */
+ err = createLppTransposer(
+ &hs->LppTrans, pSettings, hHeaderData->freqBandData.lowSubband,
+ hHeaderData->freqBandData.v_k_master, hHeaderData->freqBandData.numMaster,
+ hHeaderData->freqBandData.highSubband, timeSlots, noCols,
+ hHeaderData->freqBandData.freqBandTableNoise,
+ hHeaderData->freqBandData.nNfb, hHeaderData->sbrProcSmplRate, chan,
+ overlap);
+ if (err != SBRDEC_OK) {
+ return err;
+ }
+
+ if (flags & SBRDEC_USAC_HARMONICSBR) {
+ int noChannels, bSbr41 = flags & SBRDEC_QUAD_RATE ? 1 : 0;
+
+ noChannels =
+ QMF_SYNTH_CHANNELS /
+ ((bSbr41 + 1) * 2); /* 32 for (32:64 and 24:64) and 16 for 16:64 */
+
+ /* shared memory between hbeLightTimeDelayBuffer and hQmfHBESlotsReal if
+ * SBRDEC_HBE_ENABLE */
+ hSbrChannel->SbrDec.tmp_memory = (FIXP_DBL **)fdkCallocMatrix2D_aligned(
+ noCols, noChannels, sizeof(FIXP_DBL));
+ if (hSbrChannel->SbrDec.tmp_memory == NULL) {
+ return SBRDEC_MEM_ALLOC_FAILED;
+ }
+
+ hSbrChannel->SbrDec.hQmfHBESlotsReal = hSbrChannel->SbrDec.tmp_memory;
+ hSbrChannel->SbrDec.hQmfHBESlotsImag =
+ (FIXP_DBL **)fdkCallocMatrix2D_aligned(noCols, noChannels,
+ sizeof(FIXP_DBL));
+ if (hSbrChannel->SbrDec.hQmfHBESlotsImag == NULL) {
+ return SBRDEC_MEM_ALLOC_FAILED;
+ }
+
+ /* buffers containing unmodified qmf data; required when switching from
+ * legacy SBR to HBE */
+ /* buffer can be used as LPCFilterstates buffer because legacy SBR needs
+ * exactly these values for LPC filtering */
+ hSbrChannel->SbrDec.codecQMFBufferReal =
+ (FIXP_DBL **)fdkCallocMatrix2D_aligned(noCols, noChannels,
+ sizeof(FIXP_DBL));
+ if (hSbrChannel->SbrDec.codecQMFBufferReal == NULL) {
+ return SBRDEC_MEM_ALLOC_FAILED;
+ }
+
+ hSbrChannel->SbrDec.codecQMFBufferImag =
+ (FIXP_DBL **)fdkCallocMatrix2D_aligned(noCols, noChannels,
+ sizeof(FIXP_DBL));
+ if (hSbrChannel->SbrDec.codecQMFBufferImag == NULL) {
+ return SBRDEC_MEM_ALLOC_FAILED;
+ }
+
+ err = QmfTransposerCreate(&hs->hHBE, codecFrameSize, 0, bSbr41);
+ if (err != SBRDEC_OK) {
+ return err;
+ }
+ }
+
+ return err;
+}
+
+/*!
+ \brief Delete sbr decoder structure
+ \return errorCode, 0 if successful
+*/
+int deleteSbrDec(SBR_CHANNEL *hSbrChannel) {
+ HANDLE_SBR_DEC hs = &hSbrChannel->SbrDec;
+
+ deleteSbrEnvelopeCalc(&hs->SbrCalculateEnvelope);
+
+ if (hs->tmp_memory != NULL) {
+ FDK_FREE_MEMORY_2D_ALIGNED(hs->tmp_memory);
+ }
+
+ /* modify here */
+ FDK_FREE_MEMORY_2D_ALIGNED(hs->hQmfHBESlotsImag);
+
+ if (hs->hHBE != NULL) QmfTransposerClose(hs->hHBE);
+
+ if (hs->codecQMFBufferReal != NULL) {
+ FDK_FREE_MEMORY_2D_ALIGNED(hs->codecQMFBufferReal);
+ }
+
+ if (hs->codecQMFBufferImag != NULL) {
+ FDK_FREE_MEMORY_2D_ALIGNED(hs->codecQMFBufferImag);
+ }
+
+ return 0;
+}
+
+/*!
+ \brief resets sbr decoder structure
+ \return errorCode, 0 if successful
+*/
+SBR_ERROR
+resetSbrDec(HANDLE_SBR_DEC hSbrDec, HANDLE_SBR_HEADER_DATA hHeaderData,
+ HANDLE_SBR_PREV_FRAME_DATA hPrevFrameData, const int downsampleFac,
+ const UINT flags, HANDLE_SBR_FRAME_DATA hFrameData) {
+ SBR_ERROR sbrError = SBRDEC_OK;
+ int i;
+ FIXP_DBL *pLowBandReal[128];
+ FIXP_DBL *pLowBandImag[128];
+ int useLP = flags & SBRDEC_LOW_POWER;
+
+ int old_lsb = hSbrDec->qmfDomainInCh->fb.lsb;
+ int old_usb = hSbrDec->qmfDomainInCh->fb.usb;
+ int new_lsb = hHeaderData->freqBandData.lowSubband;
+ /* int new_usb = hHeaderData->freqBandData.highSubband; */
+ int l, startBand, stopBand, startSlot, size;
+
+ FIXP_DBL **OverlapBufferReal = hSbrDec->qmfDomainInCh->hQmfSlotsReal;
+ FIXP_DBL **OverlapBufferImag = hSbrDec->qmfDomainInCh->hQmfSlotsImag;
+
+ /* in case the previous frame was not active in terms of SBR processing, the
+ full band from 0 to no_channels was rescaled and not overwritten. Thats why
+ the scaling factor lb_scale can be seen as assigned to all bands from 0 to
+ no_channels in the previous frame. The same states for the current frame if
+ the current frame is not active in terms of SBR processing
+ */
+ int applySbrProc = (hHeaderData->syncState == SBR_ACTIVE ||
+ (hHeaderData->frameErrorFlag == 0 &&
+ hHeaderData->syncState == SBR_HEADER));
+ int applySbrProc_old = hSbrDec->applySbrProc_old;
+
+ if (!applySbrProc) {
+ new_lsb = (hSbrDec->qmfDomainInCh->fb).no_channels;
+ }
+ if (!applySbrProc_old) {
+ old_lsb = (hSbrDec->qmfDomainInCh->fb).no_channels;
+ old_usb = old_lsb;
+ }
+
+ resetSbrEnvelopeCalc(&hSbrDec->SbrCalculateEnvelope);
+
+ /* Change lsb and usb */
+ /* Synthesis */
+ FDK_ASSERT(hSbrDec->qmfDomainOutCh != NULL);
+ hSbrDec->qmfDomainOutCh->fb.lsb =
+ fixMin((INT)hSbrDec->qmfDomainOutCh->fb.no_channels,
+ (INT)hHeaderData->freqBandData.lowSubband);
+ hSbrDec->qmfDomainOutCh->fb.usb =
+ fixMin((INT)hSbrDec->qmfDomainOutCh->fb.no_channels,
+ (INT)hHeaderData->freqBandData.highSubband);
+ /* Analysis */
+ FDK_ASSERT(hSbrDec->qmfDomainInCh != NULL);
+ hSbrDec->qmfDomainInCh->fb.lsb = hSbrDec->qmfDomainOutCh->fb.lsb;
+ hSbrDec->qmfDomainInCh->fb.usb = hSbrDec->qmfDomainOutCh->fb.usb;
+
+ /*
+ The following initialization of spectral data in the overlap buffer
+ is required for dynamic x-over or a change of the start-freq for 2 reasons:
+
+ 1. If the lowband gets _wider_, unadjusted data would remain
+
+ 2. If the lowband becomes _smaller_, the highest bands of the old lowband
+ must be cleared because the whitening would be affected
+ */
+ startBand = old_lsb;
+ stopBand = new_lsb;
+ startSlot = fMax(0, hHeaderData->timeStep * (hPrevFrameData->stopPos -
+ hHeaderData->numberTimeSlots));
+ size = fMax(0, stopBand - startBand);
+
+ /* in case of USAC we don't want to zero out the memory, as this can lead to
+ holes in the spectrum; fix shall only be applied for USAC not for MPEG-4
+ SBR, in this case setting zero remains */
+ if (!(flags & SBRDEC_SYNTAX_USAC)) {
+ /* keep already adjusted data in the x-over-area */
+ if (!useLP) {
+ for (l = startSlot; l < hSbrDec->LppTrans.pSettings->overlap; l++) {
+ FDKmemclear(&OverlapBufferReal[l][startBand], size * sizeof(FIXP_DBL));
+ FDKmemclear(&OverlapBufferImag[l][startBand], size * sizeof(FIXP_DBL));
+ }
+ } else {
+ for (l = startSlot; l < hSbrDec->LppTrans.pSettings->overlap; l++) {
+ FDKmemclear(&OverlapBufferReal[l][startBand], size * sizeof(FIXP_DBL));
+ }
+ }
+
+ /*
+ reset LPC filter states
+ */
+ startBand = fixMin(old_lsb, new_lsb);
+ stopBand = fixMax(old_lsb, new_lsb);
+ size = fixMax(0, stopBand - startBand);
+
+ FDKmemclear(&hSbrDec->LppTrans.lpcFilterStatesRealLegSBR[0][startBand],
+ size * sizeof(FIXP_DBL));
+ FDKmemclear(&hSbrDec->LppTrans.lpcFilterStatesRealLegSBR[1][startBand],
+ size * sizeof(FIXP_DBL));
+ if (!useLP) {
+ FDKmemclear(&hSbrDec->LppTrans.lpcFilterStatesImagLegSBR[0][startBand],
+ size * sizeof(FIXP_DBL));
+ FDKmemclear(&hSbrDec->LppTrans.lpcFilterStatesImagLegSBR[1][startBand],
+ size * sizeof(FIXP_DBL));
+ }
+ }
+
+ if (startSlot != 0) {
+ int source_exp, target_exp, delta_exp, target_lsb, target_usb, reserve;
+ FIXP_DBL maxVal;
+
+ /*
+ Rescale already processed spectral data between old and new x-over
+ frequency. This must be done because of the separate scalefactors for
+ lowband and highband.
+ */
+
+ /* We have four relevant transitions to cover:
+ 1. old_usb is lower than new_lsb; old SBR area is completely below new SBR
+ area.
+ -> entire old area was highband and belongs to lowband now
+ and has to be rescaled.
+ 2. old_lsb is higher than new_usb; new SBR area is completely below old SBR
+ area.
+ -> old area between new_lsb and old_lsb was lowband and belongs to
+ highband now and has to be rescaled to match new highband scale.
+ 3. old_lsb is lower and old_usb is higher than new_lsb; old and new SBR
+ areas overlap.
+ -> old area between old_lsb and new_lsb was highband and belongs to
+ lowband now and has to be rescaled to match new lowband scale.
+ 4. new_lsb is lower and new_usb_is higher than old_lsb; old and new SBR
+ areas overlap.
+ -> old area between new_lsb and old_usb was lowband and belongs to
+ highband now and has to be rescaled to match new highband scale.
+ */
+
+ if (new_lsb > old_lsb) {
+ /* case 1 and 3 */
+ source_exp = SCALE2EXP(hSbrDec->qmfDomainInCh->scaling.ov_hb_scale);
+ target_exp = SCALE2EXP(hSbrDec->qmfDomainInCh->scaling.ov_lb_scale);
+
+ startBand = old_lsb;
+
+ if (new_lsb >= old_usb) {
+ /* case 1 */
+ stopBand = old_usb;
+ } else {
+ /* case 3 */
+ stopBand = new_lsb;
+ }
+
+ target_lsb = 0;
+ target_usb = old_lsb;
+ } else {
+ /* case 2 and 4 */
+ source_exp = SCALE2EXP(hSbrDec->qmfDomainInCh->scaling.ov_lb_scale);
+ target_exp = SCALE2EXP(hSbrDec->qmfDomainInCh->scaling.ov_hb_scale);
+
+ startBand = new_lsb;
+ stopBand = old_lsb;
+
+ target_lsb = old_lsb;
+ target_usb = old_usb;
+ }
+
+ maxVal =
+ maxSubbandSample(OverlapBufferReal, (useLP) ? NULL : OverlapBufferImag,
+ startBand, stopBand, 0, startSlot);
+
+ reserve = ((LONG)maxVal != 0 ? CntLeadingZeros(maxVal) - 1 : 0);
+ reserve = fixMin(
+ reserve,
+ DFRACT_BITS - 1 -
+ EXP2SCALE(
+ source_exp)); /* what is this line for, why do we need it? */
+
+ /* process only if x-over-area is not dominant after rescale;
+ otherwise I'm not sure if all buffers are scaled correctly;
+ */
+ if (target_exp - (source_exp - reserve) >= 0) {
+ rescaleSubbandSamples(OverlapBufferReal,
+ (useLP) ? NULL : OverlapBufferImag, startBand,
+ stopBand, 0, startSlot, reserve);
+ source_exp -= reserve;
+ }
+
+ delta_exp = target_exp - source_exp;
+
+ if (delta_exp < 0) { /* x-over-area is dominant */
+ startBand = target_lsb;
+ stopBand = target_usb;
+ delta_exp = -delta_exp;
+
+ if (new_lsb > old_lsb) {
+ /* The lowband has to be rescaled */
+ hSbrDec->qmfDomainInCh->scaling.ov_lb_scale = EXP2SCALE(source_exp);
+ } else {
+ /* The highband has to be rescaled */
+ hSbrDec->qmfDomainInCh->scaling.ov_hb_scale = EXP2SCALE(source_exp);
+ }
+ }
+
+ FDK_ASSERT(startBand <= stopBand);
+
+ if (!useLP) {
+ for (l = 0; l < startSlot; l++) {
+ scaleValues(OverlapBufferReal[l] + startBand, stopBand - startBand,
+ -delta_exp);
+ scaleValues(OverlapBufferImag[l] + startBand, stopBand - startBand,
+ -delta_exp);
+ }
+ } else
+ for (l = 0; l < startSlot; l++) {
+ scaleValues(OverlapBufferReal[l] + startBand, stopBand - startBand,
+ -delta_exp);
+ }
+ } /* startSlot != 0 */
+
+ /*
+ Initialize transposer and limiter
+ */
+ sbrError = resetLppTransposer(
+ &hSbrDec->LppTrans, hHeaderData->freqBandData.lowSubband,
+ hHeaderData->freqBandData.v_k_master, hHeaderData->freqBandData.numMaster,
+ hHeaderData->freqBandData.freqBandTableNoise,
+ hHeaderData->freqBandData.nNfb, hHeaderData->freqBandData.highSubband,
+ hHeaderData->sbrProcSmplRate);
+ if (sbrError != SBRDEC_OK) return sbrError;
+
+ hSbrDec->savedStates = 0;
+
+ if ((flags & SBRDEC_USAC_HARMONICSBR) && applySbrProc) {
+ sbrError = QmfTransposerReInit(hSbrDec->hHBE,
+ hHeaderData->freqBandData.freqBandTable,
+ hHeaderData->freqBandData.nSfb);
+ if (sbrError != SBRDEC_OK) return sbrError;
+
+ /* copy saved states from previous frame to legacy SBR lpc filterstate
+ * buffer */
+ for (i = 0; i < LPC_ORDER + hSbrDec->LppTrans.pSettings->overlap; i++) {
+ FDKmemcpy(
+ hSbrDec->LppTrans.lpcFilterStatesRealLegSBR[i],
+ hSbrDec->codecQMFBufferReal[hSbrDec->hHBE->noCols - LPC_ORDER -
+ hSbrDec->LppTrans.pSettings->overlap + i],
+ hSbrDec->hHBE->noChannels * sizeof(FIXP_DBL));
+ FDKmemcpy(
+ hSbrDec->LppTrans.lpcFilterStatesImagLegSBR[i],
+ hSbrDec->codecQMFBufferImag[hSbrDec->hHBE->noCols - LPC_ORDER -
+ hSbrDec->LppTrans.pSettings->overlap + i],
+ hSbrDec->hHBE->noChannels * sizeof(FIXP_DBL));
+ }
+ hSbrDec->savedStates = 1;
+
+ {
+ /* map QMF buffer to pointer array (Overlap + Frame)*/
+ for (i = 0; i < hSbrDec->LppTrans.pSettings->overlap + LPC_ORDER; i++) {
+ pLowBandReal[i] = hSbrDec->LppTrans.lpcFilterStatesRealHBE[i];
+ pLowBandImag[i] = hSbrDec->LppTrans.lpcFilterStatesImagHBE[i];
+ }
+
+ /* map QMF buffer to pointer array (Overlap + Frame)*/
+ for (i = 0; i < hSbrDec->hHBE->noCols; i++) {
+ pLowBandReal[i + hSbrDec->LppTrans.pSettings->overlap + LPC_ORDER] =
+ hSbrDec->codecQMFBufferReal[i];
+ pLowBandImag[i + hSbrDec->LppTrans.pSettings->overlap + LPC_ORDER] =
+ hSbrDec->codecQMFBufferImag[i];
+ }
+
+ if (flags & SBRDEC_QUAD_RATE) {
+ if (hFrameData->sbrPatchingMode == 0) {
+ int *xOverQmf = GetxOverBandQmfTransposer(hSbrDec->hHBE);
+
+ /* in case of harmonic SBR and no HBE_LP map additional buffer for
+ one more frame to pointer arry */
+ for (i = 0; i < hSbrDec->hHBE->noCols / 2; i++) {
+ pLowBandReal[i + hSbrDec->hHBE->noCols +
+ hSbrDec->LppTrans.pSettings->overlap + LPC_ORDER] =
+ hSbrDec->hQmfHBESlotsReal[i];
+ pLowBandImag[i + hSbrDec->hHBE->noCols +
+ hSbrDec->LppTrans.pSettings->overlap + LPC_ORDER] =
+ hSbrDec->hQmfHBESlotsImag[i];
+ }
+
+ QmfTransposerApply(
+ hSbrDec->hHBE,
+ pLowBandReal + hSbrDec->LppTrans.pSettings->overlap +
+ hSbrDec->hHBE->noCols / 2 + LPC_ORDER,
+ pLowBandImag + hSbrDec->LppTrans.pSettings->overlap +
+ hSbrDec->hHBE->noCols / 2 + LPC_ORDER,
+ hSbrDec->hHBE->noCols, pLowBandReal, pLowBandImag,
+ hSbrDec->LppTrans.lpcFilterStatesRealHBE,
+ hSbrDec->LppTrans.lpcFilterStatesImagHBE,
+ hPrevFrameData->prevSbrPitchInBins, hSbrDec->scale_lb,
+ hSbrDec->scale_hbe, &hSbrDec->qmfDomainInCh->scaling.hb_scale,
+ hHeaderData->timeStep, hFrameData->frameInfo.borders[0],
+ hSbrDec->LppTrans.pSettings->overlap, KEEP_STATES_SYNCED_OUTDIFF);
+
+ copyHarmonicSpectrum(
+ xOverQmf, pLowBandReal, pLowBandImag, hSbrDec->hHBE->noCols,
+ hSbrDec->LppTrans.pSettings->overlap, KEEP_STATES_SYNCED_OUTDIFF);
+ }
+ } else {
+ /* in case of harmonic SBR and no HBE_LP map additional buffer for
+ one more frame to pointer arry */
+ for (i = 0; i < hSbrDec->hHBE->noCols; i++) {
+ pLowBandReal[i + hSbrDec->hHBE->noCols +
+ hSbrDec->LppTrans.pSettings->overlap + LPC_ORDER] =
+ hSbrDec->hQmfHBESlotsReal[i];
+ pLowBandImag[i + hSbrDec->hHBE->noCols +
+ hSbrDec->LppTrans.pSettings->overlap + LPC_ORDER] =
+ hSbrDec->hQmfHBESlotsImag[i];
+ }
+
+ if (hFrameData->sbrPatchingMode == 0) {
+ QmfTransposerApply(
+ hSbrDec->hHBE,
+ pLowBandReal + hSbrDec->LppTrans.pSettings->overlap + LPC_ORDER,
+ pLowBandImag + hSbrDec->LppTrans.pSettings->overlap + LPC_ORDER,
+ hSbrDec->hHBE->noCols, pLowBandReal, pLowBandImag,
+ hSbrDec->LppTrans.lpcFilterStatesRealHBE,
+ hSbrDec->LppTrans.lpcFilterStatesImagHBE,
+ 0 /* not required for keeping states updated in this frame*/,
+ hSbrDec->scale_lb, hSbrDec->scale_lb,
+ &hSbrDec->qmfDomainInCh->scaling.hb_scale, hHeaderData->timeStep,
+ hFrameData->frameInfo.borders[0],
+ hSbrDec->LppTrans.pSettings->overlap, KEEP_STATES_SYNCED_NOOUT);
+ }
+
+ QmfTransposerApply(
+ hSbrDec->hHBE,
+ pLowBandReal + hSbrDec->LppTrans.pSettings->overlap +
+ hSbrDec->hHBE->noCols + LPC_ORDER,
+ pLowBandImag + hSbrDec->LppTrans.pSettings->overlap +
+ hSbrDec->hHBE->noCols + LPC_ORDER,
+ hSbrDec->hHBE->noCols, pLowBandReal, pLowBandImag,
+ hSbrDec->LppTrans.lpcFilterStatesRealHBE,
+ hSbrDec->LppTrans.lpcFilterStatesImagHBE,
+ hPrevFrameData->prevSbrPitchInBins, hSbrDec->scale_lb,
+ hSbrDec->scale_hbe, &hSbrDec->qmfDomainInCh->scaling.hb_scale,
+ hHeaderData->timeStep, hFrameData->frameInfo.borders[0],
+ hSbrDec->LppTrans.pSettings->overlap, KEEP_STATES_SYNCED_OUTDIFF);
+ }
+
+ if (hFrameData->sbrPatchingMode == 0) {
+ for (i = startSlot; i < hSbrDec->LppTrans.pSettings->overlap; i++) {
+ /*
+ Store the unmodified qmf Slots values for upper part of spectrum
+ (required for LPC filtering) required if next frame is a HBE frame
+ */
+ FDKmemcpy(hSbrDec->qmfDomainInCh->hQmfSlotsReal[i],
+ hSbrDec->LppTrans.lpcFilterStatesRealHBE[i + LPC_ORDER],
+ (64) * sizeof(FIXP_DBL));
+ FDKmemcpy(hSbrDec->qmfDomainInCh->hQmfSlotsImag[i],
+ hSbrDec->LppTrans.lpcFilterStatesImagHBE[i + LPC_ORDER],
+ (64) * sizeof(FIXP_DBL));
+ }
+
+ for (i = startSlot; i < hSbrDec->LppTrans.pSettings->overlap; i++) {
+ /*
+ Store the unmodified qmf Slots values for upper part of spectrum
+ (required for LPC filtering) required if next frame is a HBE frame
+ */
+ FDKmemcpy(
+ hSbrDec->qmfDomainInCh->hQmfSlotsReal[i],
+ hSbrDec->codecQMFBufferReal[hSbrDec->hHBE->noCols -
+ hSbrDec->LppTrans.pSettings->overlap +
+ i],
+ new_lsb * sizeof(FIXP_DBL));
+ FDKmemcpy(
+ hSbrDec->qmfDomainInCh->hQmfSlotsImag[i],
+ hSbrDec->codecQMFBufferImag[hSbrDec->hHBE->noCols -
+ hSbrDec->LppTrans.pSettings->overlap +
+ i],
+ new_lsb * sizeof(FIXP_DBL));
+ }
+ }
+ }
+ }
+
+ {
+ int adapt_lb = 0, diff = 0,
+ new_scale = hSbrDec->qmfDomainInCh->scaling.ov_lb_scale;
+
+ if ((hSbrDec->qmfDomainInCh->scaling.ov_lb_scale !=
+ hSbrDec->qmfDomainInCh->scaling.lb_scale) &&
+ startSlot != 0) {
+ /* we need to adapt spectrum to have equal scale factor, always larger
+ * than zero */
+ diff = SCALE2EXP(hSbrDec->qmfDomainInCh->scaling.ov_lb_scale) -
+ SCALE2EXP(hSbrDec->qmfDomainInCh->scaling.lb_scale);
+
+ if (diff > 0) {
+ adapt_lb = 1;
+ diff = -diff;
+ new_scale = hSbrDec->qmfDomainInCh->scaling.ov_lb_scale;
+ }
+
+ stopBand = new_lsb;
+ }
+
+ if (hFrameData->sbrPatchingMode == 1) {
+ /* scale states from LegSBR filterstates buffer */
+ for (i = 0; i < hSbrDec->LppTrans.pSettings->overlap + LPC_ORDER; i++) {
+ scaleValues(hSbrDec->LppTrans.lpcFilterStatesRealLegSBR[i], new_lsb,
+ diff);
+ if (!useLP) {
+ scaleValues(hSbrDec->LppTrans.lpcFilterStatesImagLegSBR[i], new_lsb,
+ diff);
+ }
+ }
+
+ if (flags & SBRDEC_SYNTAX_USAC) {
+ /* get missing states between old and new x_over from LegSBR
+ * filterstates buffer */
+ /* in case of legacy SBR we leave these values zeroed out */
+ for (i = startSlot; i < hSbrDec->LppTrans.pSettings->overlap; i++) {
+ FDKmemcpy(&OverlapBufferReal[i][old_lsb],
+ &hSbrDec->LppTrans
+ .lpcFilterStatesRealLegSBR[LPC_ORDER + i][old_lsb],
+ fMax(new_lsb - old_lsb, 0) * sizeof(FIXP_DBL));
+ if (!useLP) {
+ FDKmemcpy(&OverlapBufferImag[i][old_lsb],
+ &hSbrDec->LppTrans
+ .lpcFilterStatesImagLegSBR[LPC_ORDER + i][old_lsb],
+ fMax(new_lsb - old_lsb, 0) * sizeof(FIXP_DBL));
+ }
+ }
+ }
+
+ if (new_lsb > old_lsb) {
+ stopBand = old_lsb;
+ }
+ }
+ if ((adapt_lb == 1) && (stopBand > startBand)) {
+ for (l = startSlot; l < hSbrDec->LppTrans.pSettings->overlap; l++) {
+ scaleValues(OverlapBufferReal[l] + startBand, stopBand - startBand,
+ diff);
+ if (!useLP) {
+ scaleValues(OverlapBufferImag[l] + startBand, stopBand - startBand,
+ diff);
+ }
+ }
+ }
+ hSbrDec->qmfDomainInCh->scaling.ov_lb_scale = new_scale;
+ }
+
+ sbrError = ResetLimiterBands(hHeaderData->freqBandData.limiterBandTable,
+ &hHeaderData->freqBandData.noLimiterBands,
+ hHeaderData->freqBandData.freqBandTable[0],
+ hHeaderData->freqBandData.nSfb[0],
+ hSbrDec->LppTrans.pSettings->patchParam,
+ hSbrDec->LppTrans.pSettings->noOfPatches,
+ hHeaderData->bs_data.limiterBands,
+ hFrameData->sbrPatchingMode,
+ GetxOverBandQmfTransposer(hSbrDec->hHBE),
+ Get41SbrQmfTransposer(hSbrDec->hHBE));
+
+ hSbrDec->SbrCalculateEnvelope.sbrPatchingMode = hFrameData->sbrPatchingMode;
+
+ return sbrError;
+}