| /* ----------------------------------------------------------------------------- | |
| 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-D DRC decoder library ************************** | |
| Author(s): | |
| Description: | |
| *******************************************************************************/ | |
| #include "drcDec_types.h" | |
| #include "drcDec_gainDecoder.h" | |
| #include "drcGainDec_process.h" | |
| #define E_TGAINSTEP 12 | |
| static DRC_ERROR _prepareLnbIndex(ACTIVE_DRC* pActiveDrc, | |
| const int channelOffset, | |
| const int drcChannelOffset, | |
| const int numChannelsProcessed, | |
| const int lnbPointer) { | |
| int g, c; | |
| DRC_INSTRUCTIONS_UNI_DRC* pInst = pActiveDrc->pInst; | |
| /* channelOffset: start index of physical channels | |
| numChannelsProcessed: number of processed channels, physical channels and | |
| DRC channels channelOffset + drcChannelOffset: start index of DRC channels, | |
| i.e. the channel order referenced in pInst.sequenceIndex */ | |
| /* sanity checks */ | |
| if ((channelOffset + numChannelsProcessed) > 8) return DE_NOT_OK; | |
| if ((channelOffset + drcChannelOffset + numChannelsProcessed) > 8) | |
| return DE_NOT_OK; | |
| if ((channelOffset + drcChannelOffset) < 0) return DE_NOT_OK; | |
| /* prepare lnbIndexForChannel, a map of indices from each channel to its | |
| * corresponding linearNodeBuffer instance */ | |
| for (c = channelOffset; c < channelOffset + numChannelsProcessed; c++) { | |
| if (pInst->drcSetId > 0) { | |
| int drcChannel = c + drcChannelOffset; | |
| /* fallback for configuration with more physical channels than DRC | |
| channels: reuse DRC gain of first channel. This is necessary for HE-AAC | |
| mono with stereo output */ | |
| if (drcChannel >= pInst->drcChannelCount) drcChannel = 0; | |
| g = pActiveDrc->channelGroupForChannel[drcChannel]; | |
| if ((g >= 0) && !pActiveDrc->channelGroupIsParametricDrc[g]) { | |
| pActiveDrc->lnbIndexForChannel[c][lnbPointer] = | |
| pActiveDrc->activeDrcOffset + pActiveDrc->gainElementForGroup[g]; | |
| } | |
| } | |
| } | |
| return DE_OK; | |
| } | |
| static DRC_ERROR _interpolateDrcGain( | |
| const GAIN_INTERPOLATION_TYPE gainInterpolationType, | |
| const SHORT timePrev, /* time0 */ | |
| const SHORT tGainStep, /* time1 - time0 */ | |
| const SHORT start, const SHORT stop, const SHORT stepsize, | |
| const FIXP_DBL gainLeft, const FIXP_DBL gainRight, const FIXP_DBL slopeLeft, | |
| const FIXP_DBL slopeRight, FIXP_DBL* buffer) { | |
| int n, n_buf; | |
| int start_modulo, start_offset; | |
| if (tGainStep < 0) { | |
| return DE_NOT_OK; | |
| } | |
| if (tGainStep == 0) { | |
| return DE_OK; | |
| } | |
| /* get start index offset and buffer index for downsampled interpolation */ | |
| /* start_modulo = (start+timePrev)%stepsize; */ /* stepsize is a power of 2 */ | |
| start_modulo = (start + timePrev) & (stepsize - 1); | |
| start_offset = (start_modulo ? stepsize - start_modulo : 0); | |
| /* n_buf = (start + timePrev + start_offset)/stepsize; */ | |
| n_buf = (start + timePrev + start_offset) >> (15 - fixnormz_S(stepsize)); | |
| { /* gainInterpolationType == GIT_LINEAR */ | |
| LONG a; | |
| /* runs = ceil((stop - start - start_offset)/stepsize). This works for | |
| * stepsize = 2^N only. */ | |
| INT runs = (INT)(stop - start - start_offset + stepsize - 1) >> | |
| (30 - CountLeadingBits(stepsize)); | |
| INT n_min = fMin( | |
| fMin(CntLeadingZeros(gainRight), CntLeadingZeros(gainLeft)) - 1, 8); | |
| a = (LONG)((gainRight << n_min) - (gainLeft << n_min)) / tGainStep; | |
| LONG a_step = a * stepsize; | |
| n = start + start_offset; | |
| a = a * n + (LONG)(gainLeft << n_min); | |
| buffer += n_buf; | |
| #if defined(FUNCTION_interpolateDrcGain_func1) | |
| interpolateDrcGain_func1(buffer, a, a_step, n_min, runs); | |
| #else | |
| a -= a_step; | |
| n_min = 8 - n_min; | |
| for (int i = 0; i < runs; i++) { | |
| a += a_step; | |
| buffer[i] = fMultDiv2(buffer[i], (FIXP_DBL)a) << n_min; | |
| } | |
| #endif /* defined(FUNCTION_interpolateDrcGain_func1) */ | |
| } | |
| return DE_OK; | |
| } | |
| static DRC_ERROR _processNodeSegments( | |
| const int frameSize, const GAIN_INTERPOLATION_TYPE gainInterpolationType, | |
| const int nNodes, const NODE_LIN* pNodeLin, const int offset, | |
| const SHORT stepsize, | |
| const NODE_LIN nodePrevious, /* the last node of the previous frame */ | |
| const FIXP_DBL channelGain, FIXP_DBL* buffer) { | |
| DRC_ERROR err = DE_OK; | |
| SHORT timePrev, duration, start, stop, time; | |
| int n; | |
| FIXP_DBL gainLin = FL2FXCONST_DBL(1.0f / (float)(1 << 7)), gainLinPrev; | |
| FIXP_DBL slopeLin = (FIXP_DBL)0, slopeLinPrev = (FIXP_DBL)0; | |
| timePrev = nodePrevious.time + offset; | |
| gainLinPrev = nodePrevious.gainLin; | |
| for (n = 0; n < nNodes; n++) { | |
| time = pNodeLin[n].time + offset; | |
| duration = time - timePrev; | |
| gainLin = pNodeLin[n].gainLin; | |
| if (channelGain != FL2FXCONST_DBL(1.0f / (float)(1 << 8))) | |
| gainLin = | |
| SATURATE_LEFT_SHIFT(fMultDiv2(gainLin, channelGain), 9, DFRACT_BITS); | |
| if ((timePrev >= (frameSize - 1)) || | |
| (time < 0)) { /* This segment (between previous and current node) lies | |
| outside of this audio frame */ | |
| timePrev = time; | |
| gainLinPrev = gainLin; | |
| slopeLinPrev = slopeLin; | |
| continue; | |
| } | |
| /* start and stop are the boundaries of the region of this segment that lie | |
| within this audio frame. Their values are relative to the beginning of | |
| this segment. stop is the first sample that isn't processed any more. */ | |
| start = fMax(-timePrev, 1); | |
| stop = fMin(time, (SHORT)(frameSize - 1)) - timePrev + 1; | |
| err = _interpolateDrcGain(gainInterpolationType, timePrev, duration, start, | |
| stop, stepsize, gainLinPrev, gainLin, | |
| slopeLinPrev, slopeLin, buffer); | |
| if (err) return err; | |
| timePrev = time; | |
| gainLinPrev = gainLin; | |
| } | |
| return err; | |
| } | |
| /* process DRC on time-domain signal */ | |
| DRC_ERROR | |
| processDrcTime(HANDLE_DRC_GAIN_DECODER hGainDec, const int activeDrcIndex, | |
| const int delaySamples, const int channelOffset, | |
| const int drcChannelOffset, const int numChannelsProcessed, | |
| const int timeDataChannelOffset, FIXP_DBL* deinterleavedAudio) { | |
| DRC_ERROR err = DE_OK; | |
| int c, b, i; | |
| ACTIVE_DRC* pActiveDrc = &(hGainDec->activeDrc[activeDrcIndex]); | |
| DRC_GAIN_BUFFERS* pDrcGainBuffers = &(hGainDec->drcGainBuffers); | |
| int lnbPointer = pDrcGainBuffers->lnbPointer, lnbIx; | |
| LINEAR_NODE_BUFFER* pLinearNodeBuffer = pDrcGainBuffers->linearNodeBuffer; | |
| LINEAR_NODE_BUFFER* pDummyLnb = &(pDrcGainBuffers->dummyLnb); | |
| int offset = 0; | |
| if (hGainDec->delayMode == DM_REGULAR_DELAY) { | |
| offset = hGainDec->frameSize; | |
| } | |
| if ((delaySamples + offset) > | |
| (NUM_LNB_FRAMES - 2) * | |
| hGainDec->frameSize) /* if delaySamples is too big, NUM_LNB_FRAMES | |
| should be increased */ | |
| return DE_NOT_OK; | |
| err = _prepareLnbIndex(pActiveDrc, channelOffset, drcChannelOffset, | |
| numChannelsProcessed, lnbPointer); | |
| if (err) return err; | |
| deinterleavedAudio += | |
| channelOffset * timeDataChannelOffset; /* apply channelOffset */ | |
| /* signal processing loop */ | |
| for (c = channelOffset; c < channelOffset + numChannelsProcessed; c++) { | |
| if (activeDrcIndex == hGainDec->channelGainActiveDrcIndex) | |
| pDrcGainBuffers->channelGain[c][lnbPointer] = hGainDec->channelGain[c]; | |
| b = 0; | |
| { | |
| LINEAR_NODE_BUFFER *pLnb, *pLnbPrevious; | |
| NODE_LIN nodePrevious; | |
| int lnbPointerDiff; | |
| FIXP_DBL channelGain; | |
| /* get pointer to oldest linearNodes */ | |
| lnbIx = lnbPointer + 1 - NUM_LNB_FRAMES; | |
| while (lnbIx < 0) lnbIx += NUM_LNB_FRAMES; | |
| if (activeDrcIndex == hGainDec->channelGainActiveDrcIndex) | |
| channelGain = pDrcGainBuffers->channelGain[c][lnbIx]; | |
| else | |
| channelGain = FL2FXCONST_DBL(1.0f / (float)(1 << 8)); | |
| /* Loop over all node buffers in linearNodeBuffer. | |
| All nodes which are not relevant for the current frame are sorted out | |
| inside _processNodeSegments. */ | |
| for (i = 0; i < NUM_LNB_FRAMES - 1; i++) { | |
| /* Prepare previous node */ | |
| if (pActiveDrc->lnbIndexForChannel[c][lnbIx] >= 0) | |
| pLnbPrevious = &( | |
| pLinearNodeBuffer[pActiveDrc->lnbIndexForChannel[c][lnbIx] + b]); | |
| else | |
| pLnbPrevious = pDummyLnb; | |
| nodePrevious = | |
| pLnbPrevious->linearNode[lnbIx][pLnbPrevious->nNodes[lnbIx] - 1]; | |
| nodePrevious.time -= hGainDec->frameSize; | |
| if (channelGain != FL2FXCONST_DBL(1.0f / (float)(1 << 8))) | |
| nodePrevious.gainLin = SATURATE_LEFT_SHIFT( | |
| fMultDiv2(nodePrevious.gainLin, | |
| pDrcGainBuffers->channelGain[c][lnbIx]), | |
| 9, DFRACT_BITS); | |
| /* Prepare current linearNodeBuffer instance */ | |
| lnbIx++; | |
| if (lnbIx >= NUM_LNB_FRAMES) lnbIx = 0; | |
| /* if lnbIndexForChannel changes over time, use the old indices for | |
| * smooth transitions */ | |
| if (pActiveDrc->lnbIndexForChannel[c][lnbIx] >= 0) | |
| pLnb = &( | |
| pLinearNodeBuffer[pActiveDrc->lnbIndexForChannel[c][lnbIx] + b]); | |
| else /* lnbIndexForChannel = -1 means "no DRC processing", due to | |
| drcInstructionsIndex < 0, drcSetId < 0 or channel group < 0 */ | |
| pLnb = pDummyLnb; | |
| if (activeDrcIndex == hGainDec->channelGainActiveDrcIndex) | |
| channelGain = pDrcGainBuffers->channelGain[c][lnbIx]; | |
| /* number of frames of offset with respect to lnbPointer */ | |
| lnbPointerDiff = i - (NUM_LNB_FRAMES - 2); | |
| err = _processNodeSegments( | |
| hGainDec->frameSize, pLnb->gainInterpolationType, | |
| pLnb->nNodes[lnbIx], pLnb->linearNode[lnbIx], | |
| lnbPointerDiff * hGainDec->frameSize + delaySamples + offset, 1, | |
| nodePrevious, channelGain, deinterleavedAudio); | |
| if (err) return err; | |
| } | |
| deinterleavedAudio += timeDataChannelOffset; /* proceed to next channel */ | |
| } | |
| } | |
| return DE_OK; | |
| } | |
| /* process DRC on subband-domain signal */ | |
| DRC_ERROR | |
| processDrcSubband(HANDLE_DRC_GAIN_DECODER hGainDec, const int activeDrcIndex, | |
| const int delaySamples, const int channelOffset, | |
| const int drcChannelOffset, const int numChannelsProcessed, | |
| const int processSingleTimeslot, | |
| FIXP_DBL* deinterleavedAudioReal[], | |
| FIXP_DBL* deinterleavedAudioImag[]) { | |
| DRC_ERROR err = DE_OK; | |
| int b, c, g, m, m_start, m_stop, s, i; | |
| FIXP_DBL gainSb; | |
| DRC_INSTRUCTIONS_UNI_DRC* pInst = hGainDec->activeDrc[activeDrcIndex].pInst; | |
| DRC_GAIN_BUFFERS* pDrcGainBuffers = &(hGainDec->drcGainBuffers); | |
| ACTIVE_DRC* pActiveDrc = &(hGainDec->activeDrc[activeDrcIndex]); | |
| int activeDrcOffset = pActiveDrc->activeDrcOffset; | |
| int lnbPointer = pDrcGainBuffers->lnbPointer, lnbIx; | |
| LINEAR_NODE_BUFFER* pLinearNodeBuffer = pDrcGainBuffers->linearNodeBuffer; | |
| FIXP_DBL(*subbandGains)[4 * 1024 / 256] = hGainDec->subbandGains; | |
| FIXP_DBL* dummySubbandGains = hGainDec->dummySubbandGains; | |
| SUBBAND_DOMAIN_MODE subbandDomainMode = hGainDec->subbandDomainSupported; | |
| int signalIndex = 0; | |
| int frameSizeSb = 0; | |
| int nDecoderSubbands; | |
| SHORT L = 0; /* L: downsampling factor */ | |
| int offset = 0; | |
| FIXP_DBL *audioReal = NULL, *audioImag = NULL; | |
| if (hGainDec->delayMode == DM_REGULAR_DELAY) { | |
| offset = hGainDec->frameSize; | |
| } | |
| if ((delaySamples + offset) > | |
| (NUM_LNB_FRAMES - 2) * | |
| hGainDec->frameSize) /* if delaySamples is too big, NUM_LNB_FRAMES | |
| should be increased */ | |
| return DE_NOT_OK; | |
| switch (subbandDomainMode) { | |
| #if ((1024 / 256) >= (4096 / SUBBAND_DOWNSAMPLING_FACTOR_QMF64)) | |
| case SDM_QMF64: | |
| nDecoderSubbands = SUBBAND_NUM_BANDS_QMF64; | |
| L = SUBBAND_DOWNSAMPLING_FACTOR_QMF64; | |
| /* analysisDelay = SUBBAND_ANALYSIS_DELAY_QMF64; */ | |
| break; | |
| case SDM_QMF71: | |
| nDecoderSubbands = SUBBAND_NUM_BANDS_QMF71; | |
| L = SUBBAND_DOWNSAMPLING_FACTOR_QMF71; | |
| /* analysisDelay = SUBBAND_ANALYSIS_DELAY_QMF71; */ | |
| break; | |
| #else | |
| case SDM_QMF64: | |
| case SDM_QMF71: | |
| /* QMF domain processing is not supported. */ | |
| return DE_NOT_OK; | |
| #endif | |
| case SDM_STFT256: | |
| nDecoderSubbands = SUBBAND_NUM_BANDS_STFT256; | |
| L = SUBBAND_DOWNSAMPLING_FACTOR_STFT256; | |
| /* analysisDelay = SUBBAND_ANALYSIS_DELAY_STFT256; */ | |
| break; | |
| default: | |
| return DE_NOT_OK; | |
| } | |
| /* frameSizeSb = hGainDec->frameSize/L; */ /* L is a power of 2 */ | |
| frameSizeSb = | |
| hGainDec->frameSize >> (15 - fixnormz_S(L)); /* timeslots per frame */ | |
| if ((processSingleTimeslot < 0) || (processSingleTimeslot >= frameSizeSb)) { | |
| m_start = 0; | |
| m_stop = frameSizeSb; | |
| } else { | |
| m_start = processSingleTimeslot; | |
| m_stop = m_start + 1; | |
| } | |
| err = _prepareLnbIndex(pActiveDrc, channelOffset, drcChannelOffset, | |
| numChannelsProcessed, lnbPointer); | |
| if (err) return err; | |
| if (!pActiveDrc->subbandGainsReady) /* only for the first time per frame that | |
| processDrcSubband is called */ | |
| { | |
| /* write subbandGains */ | |
| for (g = 0; g < pInst->nDrcChannelGroups; g++) { | |
| b = 0; | |
| { | |
| LINEAR_NODE_BUFFER* pLnb = | |
| &(pLinearNodeBuffer[activeDrcOffset + | |
| pActiveDrc->gainElementForGroup[g] + b]); | |
| NODE_LIN nodePrevious; | |
| int lnbPointerDiff; | |
| for (m = 0; m < frameSizeSb; m++) { | |
| subbandGains[activeDrcOffset + g][b * frameSizeSb + m] = | |
| FL2FXCONST_DBL(1.0f / (float)(1 << 7)); | |
| } | |
| lnbIx = lnbPointer - (NUM_LNB_FRAMES - 1); | |
| while (lnbIx < 0) lnbIx += NUM_LNB_FRAMES; | |
| /* Loop over all node buffers in linearNodeBuffer. | |
| All nodes which are not relevant for the current frame are sorted out | |
| inside _processNodeSegments. */ | |
| for (i = 0; i < NUM_LNB_FRAMES - 1; i++) { | |
| /* Prepare previous node */ | |
| nodePrevious = pLnb->linearNode[lnbIx][pLnb->nNodes[lnbIx] - 1]; | |
| nodePrevious.time -= hGainDec->frameSize; | |
| lnbIx++; | |
| if (lnbIx >= NUM_LNB_FRAMES) lnbIx = 0; | |
| /* number of frames of offset with respect to lnbPointer */ | |
| lnbPointerDiff = i - (NUM_LNB_FRAMES - 2); | |
| err = _processNodeSegments( | |
| hGainDec->frameSize, pLnb->gainInterpolationType, | |
| pLnb->nNodes[lnbIx], pLnb->linearNode[lnbIx], | |
| lnbPointerDiff * hGainDec->frameSize + delaySamples + offset - | |
| (L - 1) / 2, | |
| L, nodePrevious, FL2FXCONST_DBL(1.0f / (float)(1 << 8)), | |
| &(subbandGains[activeDrcOffset + g][b * frameSizeSb])); | |
| if (err) return err; | |
| } | |
| } | |
| } | |
| pActiveDrc->subbandGainsReady = 1; | |
| } | |
| for (c = channelOffset; c < channelOffset + numChannelsProcessed; c++) { | |
| FIXP_DBL* thisSubbandGainsBuffer; | |
| if (pInst->drcSetId > 0) | |
| g = pActiveDrc->channelGroupForChannel[c + drcChannelOffset]; | |
| else | |
| g = -1; | |
| audioReal = deinterleavedAudioReal[signalIndex]; | |
| if (subbandDomainMode != SDM_STFT256) { | |
| audioImag = deinterleavedAudioImag[signalIndex]; | |
| } | |
| if ((g >= 0) && !pActiveDrc->channelGroupIsParametricDrc[g]) { | |
| thisSubbandGainsBuffer = subbandGains[activeDrcOffset + g]; | |
| } else { | |
| thisSubbandGainsBuffer = dummySubbandGains; | |
| } | |
| for (m = m_start; m < m_stop; m++) { | |
| INT n_min = 8; | |
| { /* single-band DRC */ | |
| gainSb = thisSubbandGainsBuffer[m]; | |
| if (activeDrcIndex == hGainDec->channelGainActiveDrcIndex) | |
| gainSb = SATURATE_LEFT_SHIFT( | |
| fMultDiv2(gainSb, hGainDec->channelGain[c]), 9, DFRACT_BITS); | |
| /* normalize gainSb for keeping signal precision */ | |
| n_min = fMin(CntLeadingZeros(gainSb) - 1, n_min); | |
| gainSb <<= n_min; | |
| n_min = 8 - n_min; | |
| if (subbandDomainMode == | |
| SDM_STFT256) { /* For STFT filterbank, real and imaginary parts are | |
| interleaved. */ | |
| for (s = 0; s < nDecoderSubbands; s++) { | |
| *audioReal = fMultDiv2(*audioReal, gainSb) << n_min; | |
| audioReal++; | |
| *audioReal = fMultDiv2(*audioReal, gainSb) << n_min; | |
| audioReal++; | |
| } | |
| } else { | |
| for (s = 0; s < nDecoderSubbands; s++) { | |
| *audioReal = fMultDiv2(*audioReal, gainSb) << n_min; | |
| audioReal++; | |
| *audioImag = fMultDiv2(*audioImag, gainSb) << n_min; | |
| audioImag++; | |
| } | |
| } | |
| } | |
| } | |
| signalIndex++; | |
| } | |
| return DE_OK; | |
| } |