From 0e5af65c467b2423a0b857ae3ad98c91acc1e190 Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Mon, 11 Nov 2019 11:38:02 +0100 Subject: 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. --- fdk-aac/libSBRenc/src/ps_main.cpp | 606 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 606 insertions(+) create mode 100644 fdk-aac/libSBRenc/src/ps_main.cpp (limited to 'fdk-aac/libSBRenc/src/ps_main.cpp') diff --git a/fdk-aac/libSBRenc/src/ps_main.cpp b/fdk-aac/libSBRenc/src/ps_main.cpp new file mode 100644 index 0000000..4d7a7a5 --- /dev/null +++ b/fdk-aac/libSBRenc/src/ps_main.cpp @@ -0,0 +1,606 @@ +/* ----------------------------------------------------------------------------- +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 encoder library ****************************** + + Author(s): M. Multrus + + Description: PS Wrapper, Downmix + +*******************************************************************************/ + +#include "ps_main.h" + +/* Includes ******************************************************************/ +#include "ps_bitenc.h" +#include "sbrenc_ram.h" + +/*--------------- function declarations --------------------*/ +static void psFindBestScaling( + HANDLE_PARAMETRIC_STEREO hParametricStereo, + FIXP_DBL *hybridData[HYBRID_FRAMESIZE][MAX_PS_CHANNELS][2], + UCHAR *dynBandScale, FIXP_DBL *maxBandValue, SCHAR *dmxScale); + +/*------------- function definitions ----------------*/ +FDK_PSENC_ERROR PSEnc_Create(HANDLE_PARAMETRIC_STEREO *phParametricStereo) { + FDK_PSENC_ERROR error = PSENC_OK; + HANDLE_PARAMETRIC_STEREO hParametricStereo = NULL; + + if (phParametricStereo == NULL) { + error = PSENC_INVALID_HANDLE; + } else { + int i; + + if (NULL == (hParametricStereo = GetRam_ParamStereo())) { + error = PSENC_MEMORY_ERROR; + goto bail; + } + FDKmemclear(hParametricStereo, sizeof(PARAMETRIC_STEREO)); + + if (PSENC_OK != + (error = FDKsbrEnc_CreatePSEncode(&hParametricStereo->hPsEncode))) { + error = PSENC_MEMORY_ERROR; + goto bail; + } + + for (i = 0; i < MAX_PS_CHANNELS; i++) { + if (FDKhybridAnalysisOpen( + &hParametricStereo->fdkHybAnaFilter[i], + hParametricStereo->__staticHybAnaStatesLF[i], + sizeof(hParametricStereo->__staticHybAnaStatesLF[i]), + hParametricStereo->__staticHybAnaStatesHF[i], + sizeof(hParametricStereo->__staticHybAnaStatesHF[i])) != 0) { + error = PSENC_MEMORY_ERROR; + goto bail; + } + } + } + +bail: + if (phParametricStereo != NULL) { + *phParametricStereo = hParametricStereo; /* return allocated handle */ + } + + if (error != PSENC_OK) { + PSEnc_Destroy(phParametricStereo); + } + return error; +} + +FDK_PSENC_ERROR PSEnc_Init(HANDLE_PARAMETRIC_STEREO hParametricStereo, + const HANDLE_PSENC_CONFIG hPsEncConfig, + INT noQmfSlots, INT noQmfBands, UCHAR *dynamic_RAM) { + FDK_PSENC_ERROR error = PSENC_OK; + + if ((NULL == hParametricStereo) || (NULL == hPsEncConfig)) { + error = PSENC_INVALID_HANDLE; + } else { + int ch, i; + + hParametricStereo->initPS = 1; + hParametricStereo->noQmfSlots = noQmfSlots; + hParametricStereo->noQmfBands = noQmfBands; + + /* clear delay lines */ + FDKmemclear(hParametricStereo->qmfDelayLines, + sizeof(hParametricStereo->qmfDelayLines)); + + hParametricStereo->qmfDelayScale = FRACT_BITS - 1; + + /* create configuration for hybrid filter bank */ + for (ch = 0; ch < MAX_PS_CHANNELS; ch++) { + FDKhybridAnalysisInit(&hParametricStereo->fdkHybAnaFilter[ch], + THREE_TO_TEN, 64, 64, 1); + } /* ch */ + + FDKhybridSynthesisInit(&hParametricStereo->fdkHybSynFilter, THREE_TO_TEN, + 64, 64); + + /* determine average delay */ + hParametricStereo->psDelay = + (HYBRID_FILTER_DELAY * hParametricStereo->noQmfBands); + + if ((hPsEncConfig->maxEnvelopes < PSENC_NENV_1) || + (hPsEncConfig->maxEnvelopes > PSENC_NENV_MAX)) { + hPsEncConfig->maxEnvelopes = PSENC_NENV_DEFAULT; + } + hParametricStereo->maxEnvelopes = hPsEncConfig->maxEnvelopes; + + if (PSENC_OK != + (error = FDKsbrEnc_InitPSEncode( + hParametricStereo->hPsEncode, (PS_BANDS)hPsEncConfig->nStereoBands, + hPsEncConfig->iidQuantErrorThreshold))) { + goto bail; + } + + for (ch = 0; ch < MAX_PS_CHANNELS; ch++) { + FIXP_DBL *pDynReal = GetRam_Sbr_envRBuffer(ch, dynamic_RAM); + FIXP_DBL *pDynImag = GetRam_Sbr_envIBuffer(ch, dynamic_RAM); + + for (i = 0; i < HYBRID_FRAMESIZE; i++) { + hParametricStereo->pHybridData[i + HYBRID_READ_OFFSET][ch][0] = + &pDynReal[i * MAX_HYBRID_BANDS]; + hParametricStereo->pHybridData[i + HYBRID_READ_OFFSET][ch][1] = + &pDynImag[i * MAX_HYBRID_BANDS]; + ; + } + + for (i = 0; i < HYBRID_READ_OFFSET; i++) { + hParametricStereo->pHybridData[i][ch][0] = + hParametricStereo->__staticHybridData[i][ch][0]; + hParametricStereo->pHybridData[i][ch][1] = + hParametricStereo->__staticHybridData[i][ch][1]; + } + } /* ch */ + + /* clear static hybrid buffer */ + FDKmemclear(hParametricStereo->__staticHybridData, + sizeof(hParametricStereo->__staticHybridData)); + + /* clear bs buffer */ + FDKmemclear(hParametricStereo->psOut, sizeof(hParametricStereo->psOut)); + + hParametricStereo->psOut[0].enablePSHeader = + 1; /* write ps header in first frame */ + + /* clear scaling buffer */ + FDKmemclear(hParametricStereo->dynBandScale, sizeof(UCHAR) * PS_MAX_BANDS); + FDKmemclear(hParametricStereo->maxBandValue, + sizeof(FIXP_DBL) * PS_MAX_BANDS); + + } /* valid handle */ +bail: + return error; +} + +FDK_PSENC_ERROR PSEnc_Destroy(HANDLE_PARAMETRIC_STEREO *phParametricStereo) { + FDK_PSENC_ERROR error = PSENC_OK; + + if (NULL != phParametricStereo) { + HANDLE_PARAMETRIC_STEREO hParametricStereo = *phParametricStereo; + if (hParametricStereo != NULL) { + FDKsbrEnc_DestroyPSEncode(&hParametricStereo->hPsEncode); + FreeRam_ParamStereo(phParametricStereo); + } + } + + return error; +} + +static FDK_PSENC_ERROR ExtractPSParameters( + HANDLE_PARAMETRIC_STEREO hParametricStereo, const int sendHeader, + FIXP_DBL *hybridData[HYBRID_FRAMESIZE][MAX_PS_CHANNELS][2]) { + FDK_PSENC_ERROR error = PSENC_OK; + + if (hParametricStereo == NULL) { + error = PSENC_INVALID_HANDLE; + } else { + /* call ps encode function */ + if (hParametricStereo->initPS) { + hParametricStereo->psOut[1] = hParametricStereo->psOut[0]; + } + hParametricStereo->psOut[0] = hParametricStereo->psOut[1]; + + if (PSENC_OK != + (error = FDKsbrEnc_PSEncode( + hParametricStereo->hPsEncode, &hParametricStereo->psOut[1], + hParametricStereo->dynBandScale, hParametricStereo->maxEnvelopes, + hybridData, hParametricStereo->noQmfSlots, sendHeader))) { + goto bail; + } + + if (hParametricStereo->initPS) { + hParametricStereo->psOut[0] = hParametricStereo->psOut[1]; + hParametricStereo->initPS = 0; + } + } +bail: + return error; +} + +static FDK_PSENC_ERROR DownmixPSQmfData( + HANDLE_PARAMETRIC_STEREO hParametricStereo, + HANDLE_QMF_FILTER_BANK sbrSynthQmf, FIXP_DBL **RESTRICT mixRealQmfData, + FIXP_DBL **RESTRICT mixImagQmfData, INT_PCM *downsampledOutSignal, + const UINT downsampledOutSignalBufSize, + FIXP_DBL *hybridData[HYBRID_FRAMESIZE][MAX_PS_CHANNELS][2], + const INT noQmfSlots, const INT psQmfScale[MAX_PS_CHANNELS], + SCHAR *qmfScale) { + FDK_PSENC_ERROR error = PSENC_OK; + + if (hParametricStereo == NULL) { + error = PSENC_INVALID_HANDLE; + } else { + int n, k; + C_AALLOC_SCRATCH_START(pWorkBuffer, FIXP_DBL, 2 * 64) + + /* define scalings */ + int dynQmfScale = fixMax( + 0, hParametricStereo->dmxScale - + 1); /* scale one bit more for addition of left and right */ + int downmixScale = psQmfScale[0] - dynQmfScale; + const FIXP_DBL maxStereoScaleFactor = MAXVAL_DBL; /* 2.f/2.f */ + + for (n = 0; n < noQmfSlots; n++) { + FIXP_DBL tmpHybrid[2][MAX_HYBRID_BANDS]; + + for (k = 0; k < 71; k++) { + int dynScale, sc; /* scaling */ + FIXP_DBL tmpLeftReal, tmpRightReal, tmpLeftImag, tmpRightImag; + FIXP_DBL tmpScaleFactor, stereoScaleFactor; + + tmpLeftReal = hybridData[n][0][0][k]; + tmpLeftImag = hybridData[n][0][1][k]; + tmpRightReal = hybridData[n][1][0][k]; + tmpRightImag = hybridData[n][1][1][k]; + + sc = fixMax( + 0, CntLeadingZeros(fixMax( + fixMax(fixp_abs(tmpLeftReal), fixp_abs(tmpLeftImag)), + fixMax(fixp_abs(tmpRightReal), fixp_abs(tmpRightImag)))) - + 2); + + tmpLeftReal <<= sc; + tmpLeftImag <<= sc; + tmpRightReal <<= sc; + tmpRightImag <<= sc; + dynScale = fixMin(sc - dynQmfScale, DFRACT_BITS - 1); + + /* calc stereo scale factor to avoid loss of energy in bands */ + /* stereo scale factor = min(2.0f, sqrt( (abs(l(k, n)^2 + abs(r(k, n)^2 + * )))/(0.5f*abs(l(k, n) + r(k, n))) )) */ + stereoScaleFactor = fPow2Div2(tmpLeftReal) + fPow2Div2(tmpLeftImag) + + fPow2Div2(tmpRightReal) + fPow2Div2(tmpRightImag); + + /* might be that tmpScaleFactor becomes negative, so fabs(.) */ + tmpScaleFactor = + fixp_abs(stereoScaleFactor + fMult(tmpLeftReal, tmpRightReal) + + fMult(tmpLeftImag, tmpRightImag)); + + /* min(2.0f, sqrt(stereoScaleFactor/(0.5f*tmpScaleFactor))) */ + if ((stereoScaleFactor >> 1) < + fMult(maxStereoScaleFactor, tmpScaleFactor)) { + int sc_num = CountLeadingBits(stereoScaleFactor); + int sc_denum = CountLeadingBits(tmpScaleFactor); + sc = -(sc_num - sc_denum); + + tmpScaleFactor = schur_div((stereoScaleFactor << (sc_num)) >> 1, + tmpScaleFactor << sc_denum, 16); + + /* prevent odd scaling for next sqrt calculation */ + if (sc & 0x1) { + sc++; + tmpScaleFactor >>= 1; + } + stereoScaleFactor = sqrtFixp(tmpScaleFactor); + stereoScaleFactor <<= (sc >> 1); + } else { + stereoScaleFactor = maxStereoScaleFactor; + } + + /* write data to hybrid output */ + tmpHybrid[0][k] = fMultDiv2(stereoScaleFactor, + (FIXP_DBL)(tmpLeftReal + tmpRightReal)) >> + dynScale; + tmpHybrid[1][k] = fMultDiv2(stereoScaleFactor, + (FIXP_DBL)(tmpLeftImag + tmpRightImag)) >> + dynScale; + + } /* hybrid bands - k */ + + FDKhybridSynthesisApply(&hParametricStereo->fdkHybSynFilter, tmpHybrid[0], + tmpHybrid[1], mixRealQmfData[n], + mixImagQmfData[n]); + + qmfSynthesisFilteringSlot( + sbrSynthQmf, mixRealQmfData[n], mixImagQmfData[n], downmixScale - 7, + downmixScale - 7, + downsampledOutSignal + (n * sbrSynthQmf->no_channels), 1, + pWorkBuffer); + + } /* slots */ + + *qmfScale = -downmixScale + 7; + + C_AALLOC_SCRATCH_END(pWorkBuffer, FIXP_DBL, 2 * 64) + + { + const INT noQmfSlots2 = hParametricStereo->noQmfSlots >> 1; + const int noQmfBands = hParametricStereo->noQmfBands; + + INT scale, i, j, slotOffset; + + FIXP_DBL tmp[2][64]; + + for (i = 0; i < noQmfSlots2; i++) { + FDKmemcpy(tmp[0], hParametricStereo->qmfDelayLines[0][i], + noQmfBands * sizeof(FIXP_DBL)); + FDKmemcpy(tmp[1], hParametricStereo->qmfDelayLines[1][i], + noQmfBands * sizeof(FIXP_DBL)); + + FDKmemcpy(hParametricStereo->qmfDelayLines[0][i], + mixRealQmfData[i + noQmfSlots2], + noQmfBands * sizeof(FIXP_DBL)); + FDKmemcpy(hParametricStereo->qmfDelayLines[1][i], + mixImagQmfData[i + noQmfSlots2], + noQmfBands * sizeof(FIXP_DBL)); + + FDKmemcpy(mixRealQmfData[i + noQmfSlots2], mixRealQmfData[i], + noQmfBands * sizeof(FIXP_DBL)); + FDKmemcpy(mixImagQmfData[i + noQmfSlots2], mixImagQmfData[i], + noQmfBands * sizeof(FIXP_DBL)); + + FDKmemcpy(mixRealQmfData[i], tmp[0], noQmfBands * sizeof(FIXP_DBL)); + FDKmemcpy(mixImagQmfData[i], tmp[1], noQmfBands * sizeof(FIXP_DBL)); + } + + if (hParametricStereo->qmfDelayScale > *qmfScale) { + scale = hParametricStereo->qmfDelayScale - *qmfScale; + slotOffset = 0; + } else { + scale = *qmfScale - hParametricStereo->qmfDelayScale; + slotOffset = noQmfSlots2; + } + + for (i = 0; i < noQmfSlots2; i++) { + for (j = 0; j < noQmfBands; j++) { + mixRealQmfData[i + slotOffset][j] >>= scale; + mixImagQmfData[i + slotOffset][j] >>= scale; + } + } + + scale = *qmfScale; + *qmfScale = fMin(*qmfScale, hParametricStereo->qmfDelayScale); + hParametricStereo->qmfDelayScale = scale; + } + + } /* valid handle */ + + return error; +} + +INT FDKsbrEnc_PSEnc_WritePSData(HANDLE_PARAMETRIC_STEREO hParametricStereo, + HANDLE_FDK_BITSTREAM hBitstream) { + return ( + (hParametricStereo != NULL) + ? FDKsbrEnc_WritePSBitstream(&hParametricStereo->psOut[0], hBitstream) + : 0); +} + +FDK_PSENC_ERROR FDKsbrEnc_PSEnc_ParametricStereoProcessing( + HANDLE_PARAMETRIC_STEREO hParametricStereo, INT_PCM *samples[2], + UINT samplesBufSize, QMF_FILTER_BANK **hQmfAnalysis, + FIXP_DBL **RESTRICT downmixedRealQmfData, + FIXP_DBL **RESTRICT downmixedImagQmfData, INT_PCM *downsampledOutSignal, + HANDLE_QMF_FILTER_BANK sbrSynthQmf, SCHAR *qmfScale, const int sendHeader) { + FDK_PSENC_ERROR error = PSENC_OK; + INT psQmfScale[MAX_PS_CHANNELS] = {0}; + int psCh, i; + C_AALLOC_SCRATCH_START(pWorkBuffer, FIXP_DBL, 4 * 64) + + for (psCh = 0; psCh < MAX_PS_CHANNELS; psCh++) { + for (i = 0; i < hQmfAnalysis[psCh]->no_col; i++) { + qmfAnalysisFilteringSlot( + hQmfAnalysis[psCh], &pWorkBuffer[2 * 64], /* qmfReal[64] */ + &pWorkBuffer[3 * 64], /* qmfImag[64] */ + samples[psCh] + i * hQmfAnalysis[psCh]->no_channels, 1, + &pWorkBuffer[0 * 64] /* qmf workbuffer 2*64 */ + ); + + FDKhybridAnalysisApply( + &hParametricStereo->fdkHybAnaFilter[psCh], + &pWorkBuffer[2 * 64], /* qmfReal[64] */ + &pWorkBuffer[3 * 64], /* qmfImag[64] */ + hParametricStereo->pHybridData[i + HYBRID_READ_OFFSET][psCh][0], + hParametricStereo->pHybridData[i + HYBRID_READ_OFFSET][psCh][1]); + + } /* no_col loop i */ + + psQmfScale[psCh] = hQmfAnalysis[psCh]->outScalefactor; + + } /* for psCh */ + + C_AALLOC_SCRATCH_END(pWorkBuffer, FIXP_DBL, 4 * 64) + + /* find best scaling in new QMF and Hybrid data */ + psFindBestScaling( + hParametricStereo, &hParametricStereo->pHybridData[HYBRID_READ_OFFSET], + hParametricStereo->dynBandScale, hParametricStereo->maxBandValue, + &hParametricStereo->dmxScale); + + /* extract the ps parameters */ + if (PSENC_OK != + (error = ExtractPSParameters(hParametricStereo, sendHeader, + &hParametricStereo->pHybridData[0]))) { + goto bail; + } + + /* save hybrid date for next frame */ + for (i = 0; i < HYBRID_READ_OFFSET; i++) { + FDKmemcpy( + hParametricStereo->pHybridData[i][0][0], + hParametricStereo->pHybridData[hParametricStereo->noQmfSlots + i][0][0], + MAX_HYBRID_BANDS * sizeof(FIXP_DBL)); /* left, real */ + FDKmemcpy( + hParametricStereo->pHybridData[i][0][1], + hParametricStereo->pHybridData[hParametricStereo->noQmfSlots + i][0][1], + MAX_HYBRID_BANDS * sizeof(FIXP_DBL)); /* left, imag */ + FDKmemcpy( + hParametricStereo->pHybridData[i][1][0], + hParametricStereo->pHybridData[hParametricStereo->noQmfSlots + i][1][0], + MAX_HYBRID_BANDS * sizeof(FIXP_DBL)); /* right, real */ + FDKmemcpy( + hParametricStereo->pHybridData[i][1][1], + hParametricStereo->pHybridData[hParametricStereo->noQmfSlots + i][1][1], + MAX_HYBRID_BANDS * sizeof(FIXP_DBL)); /* right, imag */ + } + + /* downmix and hybrid synthesis */ + if (PSENC_OK != + (error = DownmixPSQmfData( + hParametricStereo, sbrSynthQmf, downmixedRealQmfData, + downmixedImagQmfData, downsampledOutSignal, samplesBufSize, + &hParametricStereo->pHybridData[HYBRID_READ_OFFSET], + hParametricStereo->noQmfSlots, psQmfScale, qmfScale))) { + goto bail; + } + +bail: + + return error; +} + +static void psFindBestScaling( + HANDLE_PARAMETRIC_STEREO hParametricStereo, + FIXP_DBL *hybridData[HYBRID_FRAMESIZE][MAX_PS_CHANNELS][2], + UCHAR *dynBandScale, FIXP_DBL *maxBandValue, SCHAR *dmxScale) { + HANDLE_PS_ENCODE hPsEncode = hParametricStereo->hPsEncode; + + INT group, bin, col, band; + const INT frameSize = hParametricStereo->noQmfSlots; + const INT psBands = (INT)hPsEncode->psEncMode; + const INT nIidGroups = hPsEncode->nQmfIidGroups + hPsEncode->nSubQmfIidGroups; + + /* group wise scaling */ + FIXP_DBL maxVal[2][PS_MAX_BANDS]; + FIXP_DBL maxValue = FL2FXCONST_DBL(0.f); + + FDKmemclear(maxVal, sizeof(maxVal)); + + /* start with hybrid data */ + for (group = 0; group < nIidGroups; group++) { + /* Translate group to bin */ + bin = hPsEncode->subband2parameterIndex[group]; + + /* Translate from 20 bins to 10 bins */ + if (hPsEncode->psEncMode == PS_BANDS_COARSE) { + bin >>= 1; + } + + /* QMF downmix scaling */ + for (col = 0; col < frameSize; col++) { + int i, section = (col < frameSize - HYBRID_READ_OFFSET) ? 0 : 1; + FIXP_DBL tmp = maxVal[section][bin]; + for (i = hPsEncode->iidGroupBorders[group]; + i < hPsEncode->iidGroupBorders[group + 1]; i++) { + tmp = fixMax(tmp, (FIXP_DBL)fixp_abs(hybridData[col][0][0][i])); + tmp = fixMax(tmp, (FIXP_DBL)fixp_abs(hybridData[col][0][1][i])); + tmp = fixMax(tmp, (FIXP_DBL)fixp_abs(hybridData[col][1][0][i])); + tmp = fixMax(tmp, (FIXP_DBL)fixp_abs(hybridData[col][1][1][i])); + } + maxVal[section][bin] = tmp; + } + } /* nIidGroups */ + + /* convert maxSpec to maxScaling, find scaling space */ + for (band = 0; band < psBands; band++) { +#ifndef MULT_16x16 + dynBandScale[band] = + CountLeadingBits(fixMax(maxVal[0][band], maxBandValue[band])); +#else + dynBandScale[band] = fixMax( + 0, CountLeadingBits(fixMax(maxVal[0][band], maxBandValue[band])) - + FRACT_BITS); +#endif + maxValue = fixMax(maxValue, fixMax(maxVal[0][band], maxVal[1][band])); + maxBandValue[band] = fixMax(maxVal[0][band], maxVal[1][band]); + } + + /* calculate maximal scaling for QMF downmix */ +#ifndef MULT_16x16 + *dmxScale = fixMin(DFRACT_BITS, CountLeadingBits(maxValue)); +#else + *dmxScale = fixMax(0, fixMin(FRACT_BITS, CountLeadingBits((maxValue)))); +#endif +} -- cgit v1.2.3