diff options
Diffstat (limited to 'libSACdec/src/sac_process.cpp')
-rw-r--r-- | libSACdec/src/sac_process.cpp | 1066 |
1 files changed, 1066 insertions, 0 deletions
diff --git a/libSACdec/src/sac_process.cpp b/libSACdec/src/sac_process.cpp new file mode 100644 index 0000000..56c72ad --- /dev/null +++ b/libSACdec/src/sac_process.cpp @@ -0,0 +1,1066 @@ +/* ----------------------------------------------------------------------------- +Software License for The Fraunhofer FDK AAC Codec Library for Android + +© Copyright 1995 - 2018 Fraunhofer-Gesellschaft zur Förderung der angewandten +Forschung e.V. All rights reserved. + + 1. INTRODUCTION +The Fraunhofer FDK AAC Codec Library for Android ("FDK AAC Codec") is software +that implements the MPEG Advanced Audio Coding ("AAC") encoding and decoding +scheme for digital audio. This FDK AAC Codec software is intended to be used on +a wide variety of Android devices. + +AAC's HE-AAC and HE-AAC v2 versions are regarded as today's most efficient +general perceptual audio codecs. AAC-ELD is considered the best-performing +full-bandwidth communications codec by independent studies and is widely +deployed. AAC has been standardized by ISO and IEC as part of the MPEG +specifications. + +Patent licenses for necessary patent claims for the FDK AAC Codec (including +those of Fraunhofer) may be obtained through Via Licensing +(www.vialicensing.com) or through the respective patent owners individually for +the purpose of encoding or decoding bit streams in products that are compliant +with the ISO/IEC MPEG audio standards. Please note that most manufacturers of +Android devices already license these patent claims through Via Licensing or +directly from the patent owners, and therefore FDK AAC Codec software may +already be covered under those patent licenses when it is used for those +licensed purposes only. + +Commercially-licensed AAC software libraries, including floating-point versions +with enhanced sound quality, are also available from Fraunhofer. Users are +encouraged to check the Fraunhofer website for additional applications +information and documentation. + +2. COPYRIGHT LICENSE + +Redistribution and use in source and binary forms, with or without modification, +are permitted without payment of copyright license fees provided that you +satisfy the following conditions: + +You must retain the complete text of this software license in redistributions of +the FDK AAC Codec or your modifications thereto in source code form. + +You must retain the complete text of this software license in the documentation +and/or other materials provided with redistributions of the FDK AAC Codec or +your modifications thereto in binary form. You must make available free of +charge copies of the complete source code of the FDK AAC Codec and your +modifications thereto to recipients of copies in binary form. + +The name of Fraunhofer may not be used to endorse or promote products derived +from this library without prior written permission. + +You may not charge copyright license fees for anyone to use, copy or distribute +the FDK AAC Codec software or your modifications thereto. + +Your modified versions of the FDK AAC Codec must carry prominent notices stating +that you changed the software and the date of any change. For modified versions +of the FDK AAC Codec, the term "Fraunhofer FDK AAC Codec Library for Android" +must be replaced by the term "Third-Party Modified Version of the Fraunhofer FDK +AAC Codec Library for Android." + +3. NO PATENT LICENSE + +NO EXPRESS OR IMPLIED LICENSES TO ANY PATENT CLAIMS, including without +limitation the patents of Fraunhofer, ARE GRANTED BY THIS SOFTWARE LICENSE. +Fraunhofer provides no warranty of patent non-infringement with respect to this +software. + +You may use this FDK AAC Codec software or modifications thereto only for +purposes that are authorized by appropriate patent licenses. + +4. DISCLAIMER + +This FDK AAC Codec software is provided by Fraunhofer on behalf of the copyright +holders and contributors "AS IS" and WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, +including but not limited to the implied warranties of merchantability and +fitness for a particular purpose. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +CONTRIBUTORS BE LIABLE for any direct, indirect, incidental, special, exemplary, +or consequential damages, including but not limited to procurement of substitute +goods or services; loss of use, data, or profits, or business interruption, +however caused and on any theory of liability, whether in contract, strict +liability, or tort (including negligence), arising in any way out of the use of +this software, even if advised of the possibility of such damage. + +5. CONTACT INFORMATION + +Fraunhofer Institute for Integrated Circuits IIS +Attention: Audio and Multimedia Departments - FDK AAC LL +Am Wolfsmantel 33 +91058 Erlangen, Germany + +www.iis.fraunhofer.de/amm +amm-info@iis.fraunhofer.de +----------------------------------------------------------------------------- */ + +/*********************** MPEG surround decoder library ************************* + + Author(s): + + Description: SAC Processing + +*******************************************************************************/ + +/* data structures and interfaces for spatial audio reference software */ +#include "sac_process.h" + +#include "sac_bitdec.h" +#include "sac_calcM1andM2.h" +#include "sac_smoothing.h" +#include "sac_rom.h" + +#include "sac_dec_errorcodes.h" + +#include "FDK_trigFcts.h" +#include "FDK_decorrelate.h" + +/** + * \brief Linear interpolation between two parameter values. + * a*alpha + b*(1-alpha) + * = a*alpha + b - b*alpha + * + * \param alpha Weighting factor. + * \param a Parameter a. + * \param b Parameter b. + * + * \return Interpolated parameter value. + */ +FDK_INLINE FIXP_DBL interpolateParameter(const FIXP_SGL alpha, const FIXP_DBL a, + const FIXP_DBL b) { + return (b - fMult(alpha, b) + fMult(alpha, a)); +} + +/** + * \brief Map MPEG Surround channel indices to MPEG 4 PCE like channel indices. + * \param self Spatial decoder handle. + * \param ch MPEG Surround channel index. + * \return MPEG 4 PCE style channel index, corresponding to the given MPEG + * Surround channel index. + */ +static UINT mapChannel(spatialDec *self, UINT ch) { + static const UCHAR chanelIdx[][8] = { + {0, 1, 2, 3, 4, 5, 6, 7}, /* binaural, TREE_212, arbitrary tree */ + }; + + int idx = 0; + + return (chanelIdx[idx][ch]); +} + +FIXP_DBL getChGain(spatialDec *self, UINT ch, INT *scale) { + /* init no gain modifier */ + FIXP_DBL gain = 0x80000000; + *scale = 0; + + if ((!isTwoChMode(self->upmixType)) && + (self->upmixType != UPMIXTYPE_BYPASS)) { + if ((ch == 0) || (ch == 1) || (ch == 2)) { + /* no modifier */ + } + } + + return gain; +} + +SACDEC_ERROR SpatialDecQMFAnalysis(spatialDec *self, const PCM_MPS *inData, + const INT ts, const INT bypassMode, + FIXP_DBL **qmfReal, FIXP_DBL **qmfImag, + const int numInputChannels) { + SACDEC_ERROR err = MPS_OK; + int ch, offset; + + offset = self->pQmfDomain->globalConf.nBandsSynthesis * + self->pQmfDomain->globalConf.nQmfTimeSlots; + + { + for (ch = 0; ch < numInputChannels; ch++) { + const PCM_MPS *inSamples = + &inData[ts * self->pQmfDomain->globalConf.nBandsAnalysis]; + FIXP_DBL *pQmfRealAnalysis = qmfReal[ch]; /* no delay in blind mode */ + FIXP_DBL *pQmfImagAnalysis = qmfImag[ch]; + + CalculateSpaceAnalysisQmf(&self->pQmfDomain->QmfDomainIn[ch].fb, + inSamples + (ch * offset), pQmfRealAnalysis, + pQmfImagAnalysis); + + if (!isTwoChMode(self->upmixType) && !bypassMode) { + int i; + for (i = 0; i < self->qmfBands; i++) { + qmfReal[ch][i] = fMult(qmfReal[ch][i], self->clipProtectGain__FDK); + qmfImag[ch][i] = fMult(qmfImag[ch][i], self->clipProtectGain__FDK); + } + } + } + } + + self->qmfInputDelayBufPos = + (self->qmfInputDelayBufPos + 1) % self->pc_filterdelay; + + return err; +} + +SACDEC_ERROR SpatialDecFeedQMF(spatialDec *self, FIXP_DBL **qmfInDataReal, + FIXP_DBL **qmfInDataImag, const INT ts, + const INT bypassMode, FIXP_DBL **qmfReal__FDK, + FIXP_DBL **qmfImag__FDK, + const INT numInputChannels) { + SACDEC_ERROR err = MPS_OK; + int ch; + + { + for (ch = 0; ch < numInputChannels; ch++) { + FIXP_DBL *pQmfRealAnalysis = + qmfReal__FDK[ch]; /* no delay in blind mode */ + FIXP_DBL *pQmfImagAnalysis = qmfImag__FDK[ch]; + + /* Write Input data to pQmfRealAnalysis. */ + if (self->bShareDelayWithSBR) { + FDK_QmfDomain_GetSlot( + &self->pQmfDomain->QmfDomainIn[ch], ts + HYBRID_FILTER_DELAY, 0, + MAX_QMF_BANDS_TO_HYBRID, pQmfRealAnalysis, pQmfImagAnalysis, 15); + FDK_QmfDomain_GetSlot(&self->pQmfDomain->QmfDomainIn[ch], ts, + MAX_QMF_BANDS_TO_HYBRID, self->qmfBands, + pQmfRealAnalysis, pQmfImagAnalysis, 15); + } else { + FDK_QmfDomain_GetSlot(&self->pQmfDomain->QmfDomainIn[ch], ts, 0, + self->qmfBands, pQmfRealAnalysis, + pQmfImagAnalysis, 15); + } + if (ts == self->pQmfDomain->globalConf.nQmfTimeSlots - 1) { + /* Is currently also needed in case we dont have any overlap. We need to + * save lb_scale to ov_lb_scale */ + FDK_QmfDomain_SaveOverlap(&self->pQmfDomain->QmfDomainIn[ch], 0); + } + + /* Apply clip protection to output. */ + if (!isTwoChMode(self->upmixType) && !bypassMode) { + int i; + for (i = 0; i < self->qmfBands; i++) { + qmfReal__FDK[ch][i] = + fMult(qmfReal__FDK[ch][i], self->clipProtectGain__FDK); + qmfImag__FDK[ch][i] = + fMult(qmfImag__FDK[ch][i], self->clipProtectGain__FDK); + } + } + + } /* End of loop over numInputChannels */ + } + + self->qmfInputDelayBufPos = + (self->qmfInputDelayBufPos + 1) % self->pc_filterdelay; + + return err; +} + +/******************************************************************************* + Functionname: SpatialDecHybridAnalysis + ******************************************************************************* + + Description: + + Arguments: + + Input: + float** pointers[4] leftReal, leftIm, rightReal, rightIm + + Output: + float self->qmfInputReal[MAX_INPUT_CHANNELS][MAX_TIME_SLOTS][MAX_QMF_BANDS]; + float self->qmfInputImag[MAX_INPUT_CHANNELS][MAX_TIME_SLOTS][MAX_QMF_BANDS]; + + float +self->hybInputReal[MAX_INPUT_CHANNELS][MAX_TIME_SLOTS][MAX_HYBRID_BANDS]; float +self->hybInputImag[MAX_INPUT_CHANNELS][MAX_TIME_SLOTS][MAX_HYBRID_BANDS]; + + +*******************************************************************************/ +SACDEC_ERROR SpatialDecHybridAnalysis(spatialDec *self, FIXP_DBL **qmfInputReal, + FIXP_DBL **qmfInputImag, + FIXP_DBL **hybOutputReal, + FIXP_DBL **hybOutputImag, const INT ts, + const INT numInputChannels) { + SACDEC_ERROR err = MPS_OK; + int ch; + + for (ch = 0; ch < numInputChannels; + ch++) /* hybrid filtering for down-mix signals */ + { + if (self->pConfigCurrent->syntaxFlags & SACDEC_SYNTAX_LD) { + int k; + /* No hybrid filtering. Just copy the QMF data. */ + for (k = 0; k < self->hybridBands; k += 1) { + hybOutputReal[ch][k] = qmfInputReal[ch][k]; + hybOutputImag[ch][k] = qmfInputImag[ch][k]; + } + } else { + self->hybridAnalysis[ch].hfMode = self->bShareDelayWithSBR; + + if (self->stereoConfigIndex == 3) + FDK_ASSERT(self->hybridAnalysis[ch].hfMode == 0); + FDKhybridAnalysisApply(&self->hybridAnalysis[ch], qmfInputReal[ch], + qmfInputImag[ch], hybOutputReal[ch], + hybOutputImag[ch]); + } + } + + if ((self->pConfigCurrent->syntaxFlags & SACDEC_SYNTAX_USAC) && + self->residualCoding) { + self->hybridAnalysis[numInputChannels].hfMode = 0; + FDKhybridAnalysisApply( + &self->hybridAnalysis[numInputChannels], + self->qmfResidualReal__FDK[0][0], self->qmfResidualImag__FDK[0][0], + self->hybResidualReal__FDK[0], self->hybResidualImag__FDK[0]); + } + + return err; +} + +SACDEC_ERROR SpatialDecCreateX(spatialDec *self, FIXP_DBL **hybInputReal, + FIXP_DBL **hybInputImag, FIXP_DBL **pxReal, + FIXP_DBL **pxImag) { + SACDEC_ERROR err = MPS_OK; + int row; + + /* Creating wDry */ + for (row = 0; row < self->numInputChannels; row++) { + /* pointer to direct signals */ + pxReal[row] = hybInputReal[row]; + pxImag[row] = hybInputImag[row]; + } + + return err; +} + +static void M2ParamToKernelMult(FIXP_SGL *RESTRICT pKernel, + FIXP_DBL *RESTRICT Mparam, + FIXP_DBL *RESTRICT MparamPrev, + int *RESTRICT pWidth, FIXP_SGL alpha__FDK, + int nBands) { + int pb; + + for (pb = 0; pb < nBands; pb++) { + FIXP_SGL tmp = FX_DBL2FX_SGL( + interpolateParameter(alpha__FDK, Mparam[pb], MparamPrev[pb])); + + int i = pWidth[pb]; + if (i & 1) *pKernel++ = tmp; + if (i & 2) { + *pKernel++ = tmp; + *pKernel++ = tmp; + } + for (i >>= 2; i--;) { + *pKernel++ = tmp; + *pKernel++ = tmp; + *pKernel++ = tmp; + *pKernel++ = tmp; + } + } +} + +SACDEC_ERROR SpatialDecApplyM1_CreateW_Mode212( + spatialDec *self, const SPATIAL_BS_FRAME *frame, FIXP_DBL **xReal, + FIXP_DBL **xImag, FIXP_DBL **vReal, FIXP_DBL **vImag) { + SACDEC_ERROR err = MPS_OK; + int res; + FIXP_DBL *decorrInReal = vReal[0]; + FIXP_DBL *decorrInImag = vImag[0]; + + /* M1 does not do anything in 212 mode, so use simplified processing */ + FDK_ASSERT(self->numVChannels == 2); + FDK_ASSERT(self->numDirektSignals == 1); + FDK_ASSERT(self->numDecorSignals == 1); + FDKmemcpy(vReal[0], xReal[0], self->hybridBands * sizeof(FIXP_DBL)); + FDKmemcpy(vImag[0], xImag[0], self->hybridBands * sizeof(FIXP_DBL)); + + if (isTsdActive(frame->TsdData)) { + /* Generate v_{x,nonTr} as input for allpass based decorrelator */ + TsdGenerateNonTr(self->hybridBands, frame->TsdData, self->TsdTs, vReal[0], + vImag[0], vReal[1], vImag[1], &decorrInReal, + &decorrInImag); + } + /* - Decorrelate */ + res = SpatialDecGetResidualIndex(self, 1); + if (FDKdecorrelateApply(&self->apDecor[0], decorrInReal, decorrInImag, + vReal[1], vImag[1], + self->param2hyb[self->residualBands[res]])) { + return MPS_NOTOK; + } + if (isTsdActive(frame->TsdData)) { + /* Generate v_{x,Tr}, apply transient decorrelator and add to allpass based + * decorrelator output */ + TsdApply(self->hybridBands, frame->TsdData, &self->TsdTs, + vReal[0], /* input: v_x */ + vImag[0], + vReal[1], /* input: d_{x,nonTr}; output: d_{x,nonTr} + d_{x,Tr} */ + vImag[1]); + } + + /* Write residual signal in approriate parameter bands */ + if (self->residualBands[res] > 0) { + int stopBand = self->param2hyb[self->residualBands[res]]; + FDKmemcpy(vReal[1], self->hybResidualReal__FDK[res], + fixMin(stopBand, self->hybridBands) * sizeof(FIXP_DBL)); + FDKmemcpy(vImag[1], self->hybResidualImag__FDK[res], + fixMin(stopBand, self->hybridBands) * sizeof(FIXP_DBL)); + } /* (self->residualBands[res]>0) */ + + return err; +} + +SACDEC_ERROR SpatialDecApplyM2_Mode212(spatialDec *self, INT ps, + const FIXP_SGL alpha, FIXP_DBL **wReal, + FIXP_DBL **wImag, + FIXP_DBL **hybOutputRealDry, + FIXP_DBL **hybOutputImagDry) { + SACDEC_ERROR err = MPS_OK; + INT row; + + INT *pWidth = self->kernels_width; + /* for stereoConfigIndex == 3 case hybridBands is < 71 */ + INT pb_max = self->kernels[self->hybridBands - 1] + 1; + INT max_row = self->numOutputChannels; + + INT M2_exp = 0; + if (self->residualCoding) M2_exp = 3; + + for (row = 0; row < max_row; row++) // 2 times + { + FIXP_DBL *Mparam0 = self->M2Real__FDK[row][0]; + FIXP_DBL *Mparam1 = self->M2Real__FDK[row][1]; + FIXP_DBL *MparamPrev0 = self->M2RealPrev__FDK[row][0]; + FIXP_DBL *MparamPrev1 = self->M2RealPrev__FDK[row][1]; + + FIXP_DBL *RESTRICT pHybOutRealDry = hybOutputRealDry[row]; + FIXP_DBL *RESTRICT pHybOutImagDry = hybOutputImagDry[row]; + + FIXP_DBL *RESTRICT pWReal0 = wReal[0]; + FIXP_DBL *RESTRICT pWReal1 = wReal[1]; + FIXP_DBL *RESTRICT pWImag0 = wImag[0]; + FIXP_DBL *RESTRICT pWImag1 = wImag[1]; + for (INT pb = 0; pb < pb_max; pb++) { + FIXP_DBL tmp0, tmp1; + + tmp0 = interpolateParameter(alpha, Mparam0[pb], MparamPrev0[pb]); + tmp1 = interpolateParameter(alpha, Mparam1[pb], MparamPrev1[pb]); + + INT i = pWidth[pb]; + + do // about 3-4 times + { + FIXP_DBL var0, var1, real, imag; + + var0 = *pWReal0++; + var1 = *pWReal1++; + real = fMultDiv2(var0, tmp0); + var0 = *pWImag0++; + real = fMultAddDiv2(real, var1, tmp1); + var1 = *pWImag1++; + imag = fMultDiv2(var0, tmp0); + *pHybOutRealDry++ = real << (1 + M2_exp); + imag = fMultAddDiv2(imag, var1, tmp1); + *pHybOutImagDry++ = imag << (1 + M2_exp); + } while (--i != 0); + } + } + return err; +} + +SACDEC_ERROR SpatialDecApplyM2_Mode212_ResidualsPlusPhaseCoding( + spatialDec *self, INT ps, const FIXP_SGL alpha, FIXP_DBL **wReal, + FIXP_DBL **wImag, FIXP_DBL **hybOutputRealDry, + FIXP_DBL **hybOutputImagDry) { + SACDEC_ERROR err = MPS_OK; + INT row; + INT scale_param_m2; + INT *pWidth = self->kernels_width; + INT pb_max = self->kernels[self->hybridBands - 1] + 1; + + scale_param_m2 = SCALE_PARAM_M2_212_PRED + SCALE_DATA_APPLY_M2; + + for (row = 0; row < self->numM2rows; row++) { + INT qs, pb; + + FIXP_DBL *RESTRICT pWReal0 = wReal[0]; + FIXP_DBL *RESTRICT pWImag0 = wImag[0]; + FIXP_DBL *RESTRICT pWReal1 = wReal[1]; + FIXP_DBL *RESTRICT pWImag1 = wImag[1]; + + FIXP_DBL *MReal0 = self->M2Real__FDK[row][0]; + FIXP_DBL *MImag0 = self->M2Imag__FDK[row][0]; + FIXP_DBL *MReal1 = self->M2Real__FDK[row][1]; + FIXP_DBL *MRealPrev0 = self->M2RealPrev__FDK[row][0]; + FIXP_DBL *MImagPrev0 = self->M2ImagPrev__FDK[row][0]; + FIXP_DBL *MRealPrev1 = self->M2RealPrev__FDK[row][1]; + + FIXP_DBL *RESTRICT pHybOutRealDry = hybOutputRealDry[row]; + FIXP_DBL *RESTRICT pHybOutImagDry = hybOutputImagDry[row]; + + FDK_ASSERT(!(self->pConfigCurrent->syntaxFlags & SACDEC_SYNTAX_LD)); + FDK_ASSERT((pWidth[0] + pWidth[1]) >= 3); + + for (pb = 0, qs = 3; pb < 2; pb++) { + INT s; + FIXP_DBL maxVal; + FIXP_SGL mReal1; + FIXP_SGL mReal0, mImag0; + FIXP_DBL iReal0, iImag0, iReal1; + + iReal0 = interpolateParameter(alpha, MReal0[pb], MRealPrev0[pb]); + iImag0 = -interpolateParameter(alpha, MImag0[pb], MImagPrev0[pb]); + iReal1 = interpolateParameter(alpha, MReal1[pb], MRealPrev1[pb]); + + maxVal = fAbs(iReal0) | fAbs(iImag0); + maxVal |= fAbs(iReal1); + + s = fMax(CntLeadingZeros(maxVal) - 1, 0); + s = fMin(s, scale_param_m2); + + mReal0 = FX_DBL2FX_SGL(iReal0 << s); + mImag0 = FX_DBL2FX_SGL(iImag0 << s); + mReal1 = FX_DBL2FX_SGL(iReal1 << s); + + s = scale_param_m2 - s; + + INT i = pWidth[pb]; + + do { + FIXP_DBL real, imag, wReal0, wImag0, wReal1, wImag1; + + wReal0 = *pWReal0++; + wImag0 = *pWImag0++; + wReal1 = *pWReal1++; + wImag1 = *pWImag1++; + + cplxMultDiv2(&real, &imag, wReal0, wImag0, mReal0, mImag0); + + *pHybOutRealDry++ = fMultAddDiv2(real, wReal1, mReal1) << s; + *pHybOutImagDry++ = fMultAddDiv2(imag, wImag1, mReal1) << s; + + if (qs > 0) { + mImag0 = -mImag0; + qs--; + } + } while (--i != 0); + } + + for (; pb < pb_max; pb++) { + INT s; + FIXP_DBL maxVal; + FIXP_SGL mReal1; + FIXP_SGL mReal0, mImag0; + FIXP_DBL iReal0, iImag0, iReal1; + + iReal0 = interpolateParameter(alpha, MReal0[pb], MRealPrev0[pb]); + iImag0 = interpolateParameter(alpha, MImag0[pb], MImagPrev0[pb]); + iReal1 = interpolateParameter(alpha, MReal1[pb], MRealPrev1[pb]); + + maxVal = fAbs(iReal0) | fAbs(iImag0); + maxVal |= fAbs(iReal1); + + s = fMax(CntLeadingZeros(maxVal) - 1, 0); + s = fMin(s, scale_param_m2); + + mReal0 = FX_DBL2FX_SGL(iReal0 << s); + mImag0 = FX_DBL2FX_SGL(iImag0 << s); + mReal1 = FX_DBL2FX_SGL(iReal1 << s); + + s = scale_param_m2 - s; + + INT i = pWidth[pb]; + + do { + FIXP_DBL real, imag, wReal0, wImag0, wReal1, wImag1; + + wReal0 = *pWReal0++; + wImag0 = *pWImag0++; + wReal1 = *pWReal1++; + wImag1 = *pWImag1++; + + cplxMultDiv2(&real, &imag, wReal0, wImag0, mReal0, mImag0); + + *pHybOutRealDry++ = fMultAddDiv2(real, wReal1, mReal1) << s; + *pHybOutImagDry++ = fMultAddDiv2(imag, wImag1, mReal1) << s; + } while (--i != 0); + } + } + + return err; +} + +SACDEC_ERROR SpatialDecApplyM2(spatialDec *self, INT ps, const FIXP_SGL alpha, + FIXP_DBL **wReal, FIXP_DBL **wImag, + FIXP_DBL **hybOutputRealDry, + FIXP_DBL **hybOutputImagDry, + FIXP_DBL **hybOutputRealWet, + FIXP_DBL **hybOutputImagWet) { + SACDEC_ERROR err = MPS_OK; + + { + int qs, row, col; + int complexHybBands; + int complexParBands; + int scale_param_m2 = 0; + int toolsDisabled; + + UCHAR activParamBands; + FIXP_DBL *RESTRICT pWReal, *RESTRICT pWImag, *RESTRICT pHybOutRealDry, + *RESTRICT pHybOutImagDry, *RESTRICT pHybOutRealWet, + *RESTRICT pHybOutImagWet; + C_ALLOC_SCRATCH_START(pKernel, FIXP_SGL, MAX_HYBRID_BANDS); + + /* The wet signal is added to the dry signal directly in applyM2 if GES and + * STP are disabled */ + toolsDisabled = + ((self->tempShapeConfig == 1) || (self->tempShapeConfig == 2)) ? 0 : 1; + + { + complexHybBands = self->hybridBands; + complexParBands = self->numParameterBands; + } + + FDKmemclear(hybOutputImagDry[0], + self->createParams.maxNumOutputChannels * + self->createParams.maxNumCmplxHybBands * sizeof(FIXP_DBL)); + FDKmemclear(hybOutputRealDry[0], self->createParams.maxNumOutputChannels * + self->createParams.maxNumHybridBands * + sizeof(FIXP_DBL)); + + if (!toolsDisabled) { + FDKmemclear(hybOutputRealWet[0], + self->createParams.maxNumOutputChannels * + self->createParams.maxNumHybridBands * sizeof(FIXP_DBL)); + FDKmemclear(hybOutputImagWet[0], + self->createParams.maxNumOutputChannels * + self->createParams.maxNumCmplxHybBands * + sizeof(FIXP_DBL)); + } + + if (self->phaseCoding == 3) { + /* + SCALE_DATA_APPLY_M2 to compensate for Div2 below ?! */ + scale_param_m2 = SCALE_PARAM_M2_212_PRED + SCALE_DATA_APPLY_M2; + } + + for (row = 0; row < self->numM2rows; row++) { + pHybOutRealDry = hybOutputRealDry[row]; + pHybOutImagDry = hybOutputImagDry[row]; + + if (toolsDisabled) { + pHybOutRealWet = hybOutputRealDry[row]; + pHybOutImagWet = hybOutputImagDry[row]; + } else { + pHybOutRealWet = hybOutputRealWet[row]; + pHybOutImagWet = hybOutputImagWet[row]; + } + + for (col = 0; col < self->numDirektSignals; col++) { + if (self->pActivM2ParamBands == + 0) { /* default setting, calculate all rows and columns */ + activParamBands = 1; + } else { + if (self->pActivM2ParamBands[MAX_M2_INPUT * row + + col]) /* table with activ and inactiv + bands exists for current + configuration */ + activParamBands = 1; + else + activParamBands = 0; + } + if (activParamBands) { + pWReal = wReal[col]; + pWImag = wImag[col]; + + M2ParamToKernelMult(pKernel, self->M2Real__FDK[row][col], + self->M2RealPrev__FDK[row][col], + self->kernels_width, alpha, + self->numParameterBands); + + if (1 && (self->phaseCoding != 3)) { + /* direct signals */ + { + /* only one sample will be assigned to each row, hence + * accumulation is not neccessary; that is valid for all + * configurations */ + for (qs = 0; qs < complexHybBands; qs++) { + pHybOutRealDry[qs] = fMult(pWReal[qs], pKernel[qs]); + pHybOutImagDry[qs] = fMult(pWImag[qs], pKernel[qs]); + } + } + } else { /* isBinauralMode(self->upmixType) */ + + for (qs = 0; qs < complexHybBands; qs++) { + pHybOutRealDry[qs] += fMultDiv2(pWReal[qs], pKernel[qs]) + << (scale_param_m2); + pHybOutImagDry[qs] += fMultDiv2(pWImag[qs], pKernel[qs]) + << (scale_param_m2); + } + + M2ParamToKernelMult(pKernel, self->M2Imag__FDK[row][col], + self->M2ImagPrev__FDK[row][col], + self->kernels_width, alpha, complexParBands); + + /* direct signals sign is -1 for qs = 0,2 */ + pHybOutRealDry[0] += fMultDiv2(pWImag[0], pKernel[0]) + << (scale_param_m2); + pHybOutImagDry[0] -= fMultDiv2(pWReal[0], pKernel[0]) + << (scale_param_m2); + + pHybOutRealDry[2] += fMultDiv2(pWImag[2], pKernel[2]) + << (scale_param_m2); + pHybOutImagDry[2] -= fMultDiv2(pWReal[2], pKernel[2]) + << (scale_param_m2); + + /* direct signals sign is +1 for qs = 1,3,4,5,...,complexHybBands */ + pHybOutRealDry[1] -= fMultDiv2(pWImag[1], pKernel[1]) + << (scale_param_m2); + pHybOutImagDry[1] += fMultDiv2(pWReal[1], pKernel[1]) + << (scale_param_m2); + + for (qs = 3; qs < complexHybBands; qs++) { + pHybOutRealDry[qs] -= fMultDiv2(pWImag[qs], pKernel[qs]) + << (scale_param_m2); + pHybOutImagDry[qs] += fMultDiv2(pWReal[qs], pKernel[qs]) + << (scale_param_m2); + } + } /* self->upmixType */ + } /* if (activParamBands) */ + } /* self->numDirektSignals */ + + for (; col < self->numVChannels; col++) { + if (self->pActivM2ParamBands == + 0) { /* default setting, calculate all rows and columns */ + activParamBands = 1; + } else { + if (self->pActivM2ParamBands[MAX_M2_INPUT * row + + col]) /* table with activ and inactiv + bands exists for current + configuration */ + activParamBands = 1; + else + activParamBands = 0; + } + + if (activParamBands) { + int resBandIndex; + int resHybIndex; + + resBandIndex = + self->residualBands[SpatialDecGetResidualIndex(self, col)]; + resHybIndex = self->param2hyb[resBandIndex]; + + pWReal = wReal[col]; + pWImag = wImag[col]; + + M2ParamToKernelMult(pKernel, self->M2Real__FDK[row][col], + self->M2RealPrev__FDK[row][col], + self->kernels_width, alpha, + self->numParameterBands); + + if (1 && (self->phaseCoding != 3)) { + /* residual signals */ + for (qs = 0; qs < resHybIndex; qs++) { + pHybOutRealDry[qs] += fMult(pWReal[qs], pKernel[qs]); + pHybOutImagDry[qs] += fMult(pWImag[qs], pKernel[qs]); + } + /* decor signals */ + for (; qs < complexHybBands; qs++) { + pHybOutRealWet[qs] += fMult(pWReal[qs], pKernel[qs]); + pHybOutImagWet[qs] += fMult(pWImag[qs], pKernel[qs]); + } + } else { /* self->upmixType */ + /* residual signals */ + FIXP_DBL *RESTRICT pHybOutReal; + FIXP_DBL *RESTRICT pHybOutImag; + + for (qs = 0; qs < resHybIndex; qs++) { + pHybOutRealDry[qs] += fMultDiv2(pWReal[qs], pKernel[qs]) + << (scale_param_m2); + pHybOutImagDry[qs] += fMultDiv2(pWImag[qs], pKernel[qs]) + << (scale_param_m2); + } + /* decor signals */ + for (; qs < complexHybBands; qs++) { + pHybOutRealWet[qs] += fMultDiv2(pWReal[qs], pKernel[qs]) + << (scale_param_m2); + pHybOutImagWet[qs] += fMultDiv2(pWImag[qs], pKernel[qs]) + << (scale_param_m2); + } + + M2ParamToKernelMult(pKernel, self->M2Imag__FDK[row][col], + self->M2ImagPrev__FDK[row][col], + self->kernels_width, alpha, complexParBands); + + /* direct signals sign is -1 for qs = 0,2 */ + /* direct signals sign is +1 for qs = 1,3.. */ + if (toolsDisabled) { + pHybOutRealDry[0] += fMultDiv2(pWImag[0], pKernel[0]) + << (scale_param_m2); + pHybOutImagDry[0] -= fMultDiv2(pWReal[0], pKernel[0]) + << (scale_param_m2); + + pHybOutRealDry[1] -= fMultDiv2(pWImag[1], pKernel[1]) + << (scale_param_m2); + pHybOutImagDry[1] += fMultDiv2(pWReal[1], pKernel[1]) + << (scale_param_m2); + + pHybOutRealDry[2] += fMultDiv2(pWImag[2], pKernel[2]) + << (scale_param_m2); + pHybOutImagDry[2] -= fMultDiv2(pWReal[2], pKernel[2]) + << (scale_param_m2); + } else { + pHybOutReal = &pHybOutRealDry[0]; + pHybOutImag = &pHybOutImagDry[0]; + if (0 == resHybIndex) { + pHybOutReal = &pHybOutRealWet[0]; + pHybOutImag = &pHybOutImagWet[0]; + } + pHybOutReal[0] += fMultDiv2(pWImag[0], pKernel[0]) + << (scale_param_m2); + pHybOutImag[0] -= fMultDiv2(pWReal[0], pKernel[0]) + << (scale_param_m2); + + if (1 == resHybIndex) { + pHybOutReal = &pHybOutRealWet[0]; + pHybOutImag = &pHybOutImagWet[0]; + } + pHybOutReal[1] -= fMultDiv2(pWImag[1], pKernel[1]) + << (scale_param_m2); + pHybOutImag[1] += fMultDiv2(pWReal[1], pKernel[1]) + << (scale_param_m2); + + if (2 == resHybIndex) { + pHybOutReal = &pHybOutRealWet[0]; + pHybOutImag = &pHybOutImagWet[0]; + } + pHybOutReal[2] += fMultDiv2(pWImag[2], pKernel[2]) + << (scale_param_m2); + pHybOutImag[2] -= fMultDiv2(pWReal[2], pKernel[2]) + << (scale_param_m2); + } + + for (qs = 3; qs < resHybIndex; qs++) { + pHybOutRealDry[qs] -= fMultDiv2(pWImag[qs], pKernel[qs]) + << (scale_param_m2); + pHybOutImagDry[qs] += fMultDiv2(pWReal[qs], pKernel[qs]) + << (scale_param_m2); + } + /* decor signals */ + for (; qs < complexHybBands; qs++) { + pHybOutRealWet[qs] -= fMultDiv2(pWImag[qs], pKernel[qs]) + << (scale_param_m2); + pHybOutImagWet[qs] += fMultDiv2(pWReal[qs], pKernel[qs]) + << (scale_param_m2); + } + } /* self->upmixType */ + } /* if (activParamBands) { */ + } /* self->numVChannels */ + } + + C_ALLOC_SCRATCH_END(pKernel, FIXP_SGL, MAX_HYBRID_BANDS); + } + + return err; +} + +SACDEC_ERROR SpatialDecSynthesis(spatialDec *self, const INT ts, + FIXP_DBL **hybOutputReal, + FIXP_DBL **hybOutputImag, PCM_MPS *timeOut, + const INT numInputChannels, + const FDK_channelMapDescr *const mapDescr) { + SACDEC_ERROR err = MPS_OK; + + int ch; + int stride, offset; + + stride = self->numOutputChannelsAT; + offset = 1; + + PCM_MPS *pTimeOut__FDK = + &timeOut[stride * self->pQmfDomain->globalConf.nBandsSynthesis * ts]; + C_ALLOC_SCRATCH_START(pQmfReal, FIXP_DBL, QMF_MAX_SYNTHESIS_BANDS); + C_ALLOC_SCRATCH_START(pQmfImag, FIXP_DBL, QMF_MAX_SYNTHESIS_BANDS); + + for (ch = 0; ch < self->numOutputChannelsAT; ch++) { + if (self->pConfigCurrent->syntaxFlags & SACDEC_SYNTAX_LD) { + int k; + /* No hybrid filtering. Just copy the QMF data. */ + for (k = 0; k < self->hybridBands; k += 1) { + pQmfReal[k] = hybOutputReal[ch][k]; + pQmfImag[k] = hybOutputImag[ch][k]; + } + } else { + FDKhybridSynthesisApply(&self->hybridSynthesis[ch], hybOutputReal[ch], + hybOutputImag[ch], pQmfReal, pQmfImag); + } + + /* Map channel indices from MPEG Surround -> PCE style -> channelMapping[] + */ + FDK_ASSERT(self->numOutputChannelsAT <= 6); + int outCh = FDK_chMapDescr_getMapValue(mapDescr, mapChannel(self, ch), + self->numOutputChannelsAT); + + { + if (self->stereoConfigIndex == 3) { + /* MPS -> SBR */ + int i; + FIXP_DBL *pWorkBufReal, *pWorkBufImag; + FDK_ASSERT((self->pQmfDomain->QmfDomainOut[outCh].fb.outGain_m == + (FIXP_DBL)0x80000000) && + (self->pQmfDomain->QmfDomainOut[outCh].fb.outGain_e == 0)); + FDK_QmfDomain_GetWorkBuffer(&self->pQmfDomain->QmfDomainIn[outCh], ts, + &pWorkBufReal, &pWorkBufImag); + FDK_ASSERT(self->qmfBands <= + self->pQmfDomain->QmfDomainIn[outCh].workBuf_nBands); + for (i = 0; i < self->qmfBands; i++) { + pWorkBufReal[i] = pQmfReal[i]; + pWorkBufImag[i] = pQmfImag[i]; + } + self->pQmfDomain->QmfDomainIn[outCh].scaling.lb_scale = + -7; /*-ALGORITHMIC_SCALING_IN_ANALYSIS_FILTERBANK;*/ + self->pQmfDomain->QmfDomainIn[outCh].scaling.lb_scale -= + self->pQmfDomain->QmfDomainIn[outCh].fb.filterScale; + self->pQmfDomain->QmfDomainIn[outCh].scaling.lb_scale -= + self->clipProtectGainSF__FDK; + + } else { + /* Call the QMF synthesis for dry. */ + err = CalculateSpaceSynthesisQmf(&self->pQmfDomain->QmfDomainOut[outCh], + pQmfReal, pQmfImag, stride, + pTimeOut__FDK + (offset * outCh)); + } + if (err != MPS_OK) goto bail; + } + } /* ch loop */ + +bail: + C_ALLOC_SCRATCH_END(pQmfImag, FIXP_DBL, QMF_MAX_SYNTHESIS_BANDS); + C_ALLOC_SCRATCH_END(pQmfReal, FIXP_DBL, QMF_MAX_SYNTHESIS_BANDS); + + return err; +} + +void SpatialDecBufferMatrices(spatialDec *self) { + int row, col; + int complexParBands; + complexParBands = self->numParameterBands; + + /* + buffer matrices M2 + */ + for (row = 0; row < self->numM2rows; row++) { + for (col = 0; col < self->numVChannels; col++) { + FDKmemcpy(self->M2RealPrev__FDK[row][col], self->M2Real__FDK[row][col], + self->numParameterBands * sizeof(FIXP_DBL)); + if (0 || (self->phaseCoding == 3)) { + FDKmemcpy(self->M2ImagPrev__FDK[row][col], self->M2Imag__FDK[row][col], + complexParBands * sizeof(FIXP_DBL)); + } + } + } + + /* buffer phase */ + FDKmemcpy(self->PhasePrevLeft__FDK, self->PhaseLeft__FDK, + self->numParameterBands * sizeof(FIXP_DBL)); + FDKmemcpy(self->PhasePrevRight__FDK, self->PhaseRight__FDK, + self->numParameterBands * sizeof(FIXP_DBL)); +} + +#define PHASE_SCALE 2 + +#ifndef P_PI +#define P_PI 3.1415926535897932 +#endif + +/* For better precision, PI (pi_x2) is already doubled */ +static FIXP_DBL interp_angle__FDK(FIXP_DBL angle1, FIXP_DBL angle2, + FIXP_SGL alpha, FIXP_DBL pi_x2) { + if (angle2 - angle1 > (pi_x2 >> 1)) angle2 -= pi_x2; + + if (angle1 - angle2 > (pi_x2 >> 1)) angle1 -= pi_x2; + + return interpolateParameter(alpha, angle2, angle1); +} + +/* + * + */ +void SpatialDecApplyPhase(spatialDec *self, FIXP_SGL alpha__FDK, + int lastSlotOfParamSet) { + int pb, qs; + FIXP_DBL ppb[MAX_PARAMETER_BANDS * + 4]; /* left real, imag - right real, imag interleaved */ + + const FIXP_DBL pi_x2 = PIx2__IPD; + for (pb = 0; pb < self->numParameterBands; pb++) { + FIXP_DBL pl, pr; + + pl = interp_angle__FDK(self->PhasePrevLeft__FDK[pb], + self->PhaseLeft__FDK[pb], alpha__FDK, pi_x2); + pr = interp_angle__FDK(self->PhasePrevRight__FDK[pb], + self->PhaseRight__FDK[pb], alpha__FDK, pi_x2); + + inline_fixp_cos_sin(pl, pr, IPD_SCALE, &ppb[4 * pb]); + } + + /* sign is -1 for qs = 0,2 and +1 for qs = 1 */ + + const SCHAR *kernels = &self->kernels[0]; + + FIXP_DBL *Dry_real0 = &self->hybOutputRealDry__FDK[0][0]; + FIXP_DBL *Dry_imag0 = &self->hybOutputImagDry__FDK[0][0]; + FIXP_DBL *Dry_real1 = &self->hybOutputRealDry__FDK[1][0]; + FIXP_DBL *Dry_imag1 = &self->hybOutputImagDry__FDK[1][0]; + + for (qs = 2; qs >= 0; qs--) { + FIXP_DBL out_re, out_im; + + pb = *kernels++; + if (qs == 1) /* sign[qs] >= 0 */ + { + cplxMultDiv2(&out_re, &out_im, *Dry_real0, *Dry_imag0, ppb[4 * pb + 0], + ppb[4 * pb + 1]); + out_re <<= PHASE_SCALE - 1; + out_im <<= PHASE_SCALE - 1; + *Dry_real0++ = out_re; + *Dry_imag0++ = out_im; + + cplxMultDiv2(&out_re, &out_im, *Dry_real1, *Dry_imag1, ppb[4 * pb + 2], + ppb[4 * pb + 3]); + out_re <<= PHASE_SCALE - 1; + out_im <<= PHASE_SCALE - 1; + *Dry_real1++ = out_re; + *Dry_imag1++ = out_im; + } else { + cplxMultDiv2(&out_re, &out_im, *Dry_real0, *Dry_imag0, ppb[4 * pb + 0], + -ppb[4 * pb + 1]); + out_re <<= PHASE_SCALE - 1; + out_im <<= PHASE_SCALE - 1; + *Dry_real0++ = out_re; + *Dry_imag0++ = out_im; + + cplxMultDiv2(&out_re, &out_im, *Dry_real1, *Dry_imag1, ppb[4 * pb + 2], + -ppb[4 * pb + 3]); + out_re <<= PHASE_SCALE - 1; + out_im <<= PHASE_SCALE - 1; + *Dry_real1++ = out_re; + *Dry_imag1++ = out_im; + } + } + + /* sign is +1 for qs >=3 */ + for (qs = self->hybridBands - 3; qs--;) { + FIXP_DBL out_re, out_im; + + pb = *kernels++; + cplxMultDiv2(&out_re, &out_im, *Dry_real0, *Dry_imag0, ppb[4 * pb + 0], + ppb[4 * pb + 1]); + out_re <<= PHASE_SCALE - 1; + out_im <<= PHASE_SCALE - 1; + *Dry_real0++ = out_re; + *Dry_imag0++ = out_im; + + cplxMultDiv2(&out_re, &out_im, *Dry_real1, *Dry_imag1, ppb[4 * pb + 2], + ppb[4 * pb + 3]); + out_re <<= PHASE_SCALE - 1; + out_im <<= PHASE_SCALE - 1; + *Dry_real1++ = out_re; + *Dry_imag1++ = out_im; + } +} |