/* -----------------------------------------------------------------------------
|
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;
|
}
|