• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* -----------------------------------------------------------------------------
2 Software License for The Fraunhofer FDK AAC Codec Library for Android
3 
4 © Copyright  1995 - 2018 Fraunhofer-Gesellschaft zur Förderung der angewandten
5 Forschung e.V. All rights reserved.
6 
7  1.    INTRODUCTION
8 The Fraunhofer FDK AAC Codec Library for Android ("FDK AAC Codec") is software
9 that implements the MPEG Advanced Audio Coding ("AAC") encoding and decoding
10 scheme for digital audio. This FDK AAC Codec software is intended to be used on
11 a wide variety of Android devices.
12 
13 AAC's HE-AAC and HE-AAC v2 versions are regarded as today's most efficient
14 general perceptual audio codecs. AAC-ELD is considered the best-performing
15 full-bandwidth communications codec by independent studies and is widely
16 deployed. AAC has been standardized by ISO and IEC as part of the MPEG
17 specifications.
18 
19 Patent licenses for necessary patent claims for the FDK AAC Codec (including
20 those of Fraunhofer) may be obtained through Via Licensing
21 (www.vialicensing.com) or through the respective patent owners individually for
22 the purpose of encoding or decoding bit streams in products that are compliant
23 with the ISO/IEC MPEG audio standards. Please note that most manufacturers of
24 Android devices already license these patent claims through Via Licensing or
25 directly from the patent owners, and therefore FDK AAC Codec software may
26 already be covered under those patent licenses when it is used for those
27 licensed purposes only.
28 
29 Commercially-licensed AAC software libraries, including floating-point versions
30 with enhanced sound quality, are also available from Fraunhofer. Users are
31 encouraged to check the Fraunhofer website for additional applications
32 information and documentation.
33 
34 2.    COPYRIGHT LICENSE
35 
36 Redistribution and use in source and binary forms, with or without modification,
37 are permitted without payment of copyright license fees provided that you
38 satisfy the following conditions:
39 
40 You must retain the complete text of this software license in redistributions of
41 the FDK AAC Codec or your modifications thereto in source code form.
42 
43 You must retain the complete text of this software license in the documentation
44 and/or other materials provided with redistributions of the FDK AAC Codec or
45 your modifications thereto in binary form. You must make available free of
46 charge copies of the complete source code of the FDK AAC Codec and your
47 modifications thereto to recipients of copies in binary form.
48 
49 The name of Fraunhofer may not be used to endorse or promote products derived
50 from this library without prior written permission.
51 
52 You may not charge copyright license fees for anyone to use, copy or distribute
53 the FDK AAC Codec software or your modifications thereto.
54 
55 Your modified versions of the FDK AAC Codec must carry prominent notices stating
56 that you changed the software and the date of any change. For modified versions
57 of the FDK AAC Codec, the term "Fraunhofer FDK AAC Codec Library for Android"
58 must be replaced by the term "Third-Party Modified Version of the Fraunhofer FDK
59 AAC Codec Library for Android."
60 
61 3.    NO PATENT LICENSE
62 
63 NO EXPRESS OR IMPLIED LICENSES TO ANY PATENT CLAIMS, including without
64 limitation the patents of Fraunhofer, ARE GRANTED BY THIS SOFTWARE LICENSE.
65 Fraunhofer provides no warranty of patent non-infringement with respect to this
66 software.
67 
68 You may use this FDK AAC Codec software or modifications thereto only for
69 purposes that are authorized by appropriate patent licenses.
70 
71 4.    DISCLAIMER
72 
73 This FDK AAC Codec software is provided by Fraunhofer on behalf of the copyright
74 holders and contributors "AS IS" and WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
75 including but not limited to the implied warranties of merchantability and
76 fitness for a particular purpose. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
77 CONTRIBUTORS BE LIABLE for any direct, indirect, incidental, special, exemplary,
78 or consequential damages, including but not limited to procurement of substitute
79 goods or services; loss of use, data, or profits, or business interruption,
80 however caused and on any theory of liability, whether in contract, strict
81 liability, or tort (including negligence), arising in any way out of the use of
82 this software, even if advised of the possibility of such damage.
83 
84 5.    CONTACT INFORMATION
85 
86 Fraunhofer Institute for Integrated Circuits IIS
87 Attention: Audio and Multimedia Departments - FDK AAC LL
88 Am Wolfsmantel 33
89 91058 Erlangen, Germany
90 
91 www.iis.fraunhofer.de/amm
92 amm-info@iis.fraunhofer.de
93 ----------------------------------------------------------------------------- */
94 
95 /************************* MPEG-D DRC decoder library **************************
96 
97    Author(s):
98 
99    Description:
100 
101 *******************************************************************************/
102 
103 #include "drcDec_types.h"
104 #include "drcDec_gainDecoder.h"
105 #include "drcGainDec_process.h"
106 
107 #define E_TGAINSTEP 12
108 
_prepareLnbIndex(ACTIVE_DRC * pActiveDrc,const int channelOffset,const int drcChannelOffset,const int numChannelsProcessed,const int lnbPointer)109 static DRC_ERROR _prepareLnbIndex(ACTIVE_DRC* pActiveDrc,
110                                   const int channelOffset,
111                                   const int drcChannelOffset,
112                                   const int numChannelsProcessed,
113                                   const int lnbPointer) {
114   int g, c;
115   DRC_INSTRUCTIONS_UNI_DRC* pInst = pActiveDrc->pInst;
116 
117   /* channelOffset: start index of physical channels
118      numChannelsProcessed: number of processed channels, physical channels and
119      DRC channels channelOffset + drcChannelOffset: start index of DRC channels,
120         i.e. the channel order referenced in pInst.sequenceIndex */
121 
122   /* sanity checks */
123   if ((channelOffset + numChannelsProcessed) > 8) return DE_NOT_OK;
124 
125   if ((channelOffset + drcChannelOffset + numChannelsProcessed) > 8)
126     return DE_NOT_OK;
127 
128   if ((channelOffset + drcChannelOffset) < 0) return DE_NOT_OK;
129 
130   /* prepare lnbIndexForChannel, a map of indices from each channel to its
131    * corresponding linearNodeBuffer instance */
132   for (c = channelOffset; c < channelOffset + numChannelsProcessed; c++) {
133     if (pInst->drcSetId > 0) {
134       int drcChannel = c + drcChannelOffset;
135       /* fallback for configuration with more physical channels than DRC
136          channels: reuse DRC gain of first channel. This is necessary for HE-AAC
137          mono with stereo output */
138       if (drcChannel >= pInst->drcChannelCount) drcChannel = 0;
139       g = pActiveDrc->channelGroupForChannel[drcChannel];
140       if ((g >= 0) && !pActiveDrc->channelGroupIsParametricDrc[g]) {
141         pActiveDrc->lnbIndexForChannel[c][lnbPointer] =
142             pActiveDrc->activeDrcOffset + pActiveDrc->gainElementForGroup[g];
143       }
144     }
145   }
146 
147   return DE_OK;
148 }
149 
_interpolateDrcGain(const GAIN_INTERPOLATION_TYPE gainInterpolationType,const SHORT timePrev,const SHORT tGainStep,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)150 static DRC_ERROR _interpolateDrcGain(
151     const GAIN_INTERPOLATION_TYPE gainInterpolationType,
152     const SHORT timePrev,  /* time0 */
153     const SHORT tGainStep, /* time1 - time0 */
154     const SHORT start, const SHORT stop, const SHORT stepsize,
155     const FIXP_DBL gainLeft, const FIXP_DBL gainRight, const FIXP_DBL slopeLeft,
156     const FIXP_DBL slopeRight, FIXP_DBL* buffer) {
157   int n, n_buf;
158   int start_modulo, start_offset;
159 
160   if (tGainStep < 0) {
161     return DE_NOT_OK;
162   }
163   if (tGainStep == 0) {
164     return DE_OK;
165   }
166 
167   /* get start index offset and buffer index for downsampled interpolation */
168   /* start_modulo = (start+timePrev)%stepsize; */ /* stepsize is a power of 2 */
169   start_modulo = (start + timePrev) & (stepsize - 1);
170   start_offset = (start_modulo ? stepsize - start_modulo : 0);
171   /* n_buf = (start + timePrev + start_offset)/stepsize; */
172   n_buf = (start + timePrev + start_offset) >> (15 - fixnormz_S(stepsize));
173 
174   { /* gainInterpolationType == GIT_LINEAR */
175     LONG a;
176     /* runs = ceil((stop - start - start_offset)/stepsize). This works for
177      * stepsize = 2^N only. */
178     INT runs = (INT)(stop - start - start_offset + stepsize - 1) >>
179                (30 - CountLeadingBits(stepsize));
180     INT n_min = fMin(
181         fMin(CntLeadingZeros(gainRight), CntLeadingZeros(gainLeft)) - 1, 8);
182     a = (LONG)((gainRight << n_min) - (gainLeft << n_min)) / tGainStep;
183     LONG a_step = a * stepsize;
184     n = start + start_offset;
185     a = a * n + (LONG)(gainLeft << n_min);
186     buffer += n_buf;
187 #if defined(FUNCTION_interpolateDrcGain_func1)
188     interpolateDrcGain_func1(buffer, a, a_step, n_min, runs);
189 #else
190     a -= a_step;
191     n_min = 8 - n_min;
192     for (int i = 0; i < runs; i++) {
193       a += a_step;
194       buffer[i] = fMultDiv2(buffer[i], (FIXP_DBL)a) << n_min;
195     }
196 #endif /* defined(FUNCTION_interpolateDrcGain_func1) */
197   }
198   return DE_OK;
199 }
200 
_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,const FIXP_DBL channelGain,FIXP_DBL * buffer)201 static DRC_ERROR _processNodeSegments(
202     const int frameSize, const GAIN_INTERPOLATION_TYPE gainInterpolationType,
203     const int nNodes, const NODE_LIN* pNodeLin, const int offset,
204     const SHORT stepsize,
205     const NODE_LIN nodePrevious, /* the last node of the previous frame */
206     const FIXP_DBL channelGain, FIXP_DBL* buffer) {
207   DRC_ERROR err = DE_OK;
208   SHORT timePrev, duration, start, stop, time;
209   int n;
210   FIXP_DBL gainLin = FL2FXCONST_DBL(1.0f / (float)(1 << 7)), gainLinPrev;
211   FIXP_DBL slopeLin = (FIXP_DBL)0, slopeLinPrev = (FIXP_DBL)0;
212 
213   timePrev = nodePrevious.time + offset;
214   gainLinPrev = nodePrevious.gainLin;
215   for (n = 0; n < nNodes; n++) {
216     time = pNodeLin[n].time + offset;
217     duration = time - timePrev;
218     gainLin = pNodeLin[n].gainLin;
219     if (channelGain != FL2FXCONST_DBL(1.0f / (float)(1 << 8)))
220       gainLin =
221           SATURATE_LEFT_SHIFT(fMultDiv2(gainLin, channelGain), 9, DFRACT_BITS);
222 
223     if ((timePrev >= (frameSize - 1)) ||
224         (time < 0)) { /* This segment (between previous and current node) lies
225                          outside of this audio frame */
226       timePrev = time;
227       gainLinPrev = gainLin;
228       slopeLinPrev = slopeLin;
229       continue;
230     }
231 
232     /* start and stop are the boundaries of the region of this segment that lie
233        within this audio frame. Their values are relative to the beginning of
234        this segment. stop is the first sample that isn't processed any more. */
235     start = fMax(-timePrev, 1);
236     stop = fMin(time, (SHORT)(frameSize - 1)) - timePrev + 1;
237 
238     err = _interpolateDrcGain(gainInterpolationType, timePrev, duration, start,
239                               stop, stepsize, gainLinPrev, gainLin,
240                               slopeLinPrev, slopeLin, buffer);
241     if (err) return err;
242 
243     timePrev = time;
244     gainLinPrev = gainLin;
245   }
246   return err;
247 }
248 
249 /* process DRC on time-domain signal */
250 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)251 processDrcTime(HANDLE_DRC_GAIN_DECODER hGainDec, const int activeDrcIndex,
252                const int delaySamples, const int channelOffset,
253                const int drcChannelOffset, const int numChannelsProcessed,
254                const int timeDataChannelOffset, FIXP_DBL* deinterleavedAudio) {
255   DRC_ERROR err = DE_OK;
256   int c, b, i;
257   ACTIVE_DRC* pActiveDrc = &(hGainDec->activeDrc[activeDrcIndex]);
258   DRC_GAIN_BUFFERS* pDrcGainBuffers = &(hGainDec->drcGainBuffers);
259   int lnbPointer = pDrcGainBuffers->lnbPointer, lnbIx;
260   LINEAR_NODE_BUFFER* pLinearNodeBuffer = pDrcGainBuffers->linearNodeBuffer;
261   LINEAR_NODE_BUFFER* pDummyLnb = &(pDrcGainBuffers->dummyLnb);
262   int offset = 0;
263 
264   if (hGainDec->delayMode == DM_REGULAR_DELAY) {
265     offset = hGainDec->frameSize;
266   }
267 
268   if ((delaySamples + offset) >
269       (NUM_LNB_FRAMES - 2) *
270           hGainDec->frameSize) /* if delaySamples is too big, NUM_LNB_FRAMES
271                                   should be increased */
272     return DE_NOT_OK;
273 
274   err = _prepareLnbIndex(pActiveDrc, channelOffset, drcChannelOffset,
275                          numChannelsProcessed, lnbPointer);
276   if (err) return err;
277 
278   deinterleavedAudio +=
279       channelOffset * timeDataChannelOffset; /* apply channelOffset */
280 
281   /* signal processing loop */
282   for (c = channelOffset; c < channelOffset + numChannelsProcessed; c++) {
283     if (activeDrcIndex == hGainDec->channelGainActiveDrcIndex)
284       pDrcGainBuffers->channelGain[c][lnbPointer] = hGainDec->channelGain[c];
285 
286     b = 0;
287     {
288       LINEAR_NODE_BUFFER *pLnb, *pLnbPrevious;
289       NODE_LIN nodePrevious;
290       int lnbPointerDiff;
291       FIXP_DBL channelGain;
292       /* get pointer to oldest linearNodes */
293       lnbIx = lnbPointer + 1 - NUM_LNB_FRAMES;
294       while (lnbIx < 0) lnbIx += NUM_LNB_FRAMES;
295 
296       if (activeDrcIndex == hGainDec->channelGainActiveDrcIndex)
297         channelGain = pDrcGainBuffers->channelGain[c][lnbIx];
298       else
299         channelGain = FL2FXCONST_DBL(1.0f / (float)(1 << 8));
300 
301       /* Loop over all node buffers in linearNodeBuffer.
302          All nodes which are not relevant for the current frame are sorted out
303          inside _processNodeSegments. */
304       for (i = 0; i < NUM_LNB_FRAMES - 1; i++) {
305         /* Prepare previous node */
306         if (pActiveDrc->lnbIndexForChannel[c][lnbIx] >= 0)
307           pLnbPrevious = &(
308               pLinearNodeBuffer[pActiveDrc->lnbIndexForChannel[c][lnbIx] + b]);
309         else
310           pLnbPrevious = pDummyLnb;
311         nodePrevious =
312             pLnbPrevious->linearNode[lnbIx][pLnbPrevious->nNodes[lnbIx] - 1];
313         nodePrevious.time -= hGainDec->frameSize;
314         if (channelGain != FL2FXCONST_DBL(1.0f / (float)(1 << 8)))
315           nodePrevious.gainLin = SATURATE_LEFT_SHIFT(
316               fMultDiv2(nodePrevious.gainLin,
317                         pDrcGainBuffers->channelGain[c][lnbIx]),
318               9, DFRACT_BITS);
319 
320         /* Prepare current linearNodeBuffer instance */
321         lnbIx++;
322         if (lnbIx >= NUM_LNB_FRAMES) lnbIx = 0;
323 
324         /* if lnbIndexForChannel changes over time, use the old indices for
325          * smooth transitions */
326         if (pActiveDrc->lnbIndexForChannel[c][lnbIx] >= 0)
327           pLnb = &(
328               pLinearNodeBuffer[pActiveDrc->lnbIndexForChannel[c][lnbIx] + b]);
329         else /* lnbIndexForChannel = -1 means "no DRC processing", due to
330                 drcInstructionsIndex < 0, drcSetId < 0 or channel group < 0 */
331           pLnb = pDummyLnb;
332 
333         if (activeDrcIndex == hGainDec->channelGainActiveDrcIndex)
334           channelGain = pDrcGainBuffers->channelGain[c][lnbIx];
335 
336         /* number of frames of offset with respect to lnbPointer */
337         lnbPointerDiff = i - (NUM_LNB_FRAMES - 2);
338 
339         err = _processNodeSegments(
340             hGainDec->frameSize, pLnb->gainInterpolationType,
341             pLnb->nNodes[lnbIx], pLnb->linearNode[lnbIx],
342             lnbPointerDiff * hGainDec->frameSize + delaySamples + offset, 1,
343             nodePrevious, channelGain, deinterleavedAudio);
344         if (err) return err;
345       }
346       deinterleavedAudio += timeDataChannelOffset; /* proceed to next channel */
347     }
348   }
349   return DE_OK;
350 }
351 
352 /* process DRC on subband-domain signal */
353 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[])354 processDrcSubband(HANDLE_DRC_GAIN_DECODER hGainDec, const int activeDrcIndex,
355                   const int delaySamples, const int channelOffset,
356                   const int drcChannelOffset, const int numChannelsProcessed,
357                   const int processSingleTimeslot,
358                   FIXP_DBL* deinterleavedAudioReal[],
359                   FIXP_DBL* deinterleavedAudioImag[]) {
360   DRC_ERROR err = DE_OK;
361   int b, c, g, m, m_start, m_stop, s, i;
362   FIXP_DBL gainSb;
363   DRC_INSTRUCTIONS_UNI_DRC* pInst = hGainDec->activeDrc[activeDrcIndex].pInst;
364   DRC_GAIN_BUFFERS* pDrcGainBuffers = &(hGainDec->drcGainBuffers);
365   ACTIVE_DRC* pActiveDrc = &(hGainDec->activeDrc[activeDrcIndex]);
366   int activeDrcOffset = pActiveDrc->activeDrcOffset;
367   int lnbPointer = pDrcGainBuffers->lnbPointer, lnbIx;
368   LINEAR_NODE_BUFFER* pLinearNodeBuffer = pDrcGainBuffers->linearNodeBuffer;
369   FIXP_DBL(*subbandGains)[4 * 1024 / 256] = hGainDec->subbandGains;
370   FIXP_DBL* dummySubbandGains = hGainDec->dummySubbandGains;
371   SUBBAND_DOMAIN_MODE subbandDomainMode = hGainDec->subbandDomainSupported;
372   int signalIndex = 0;
373   int frameSizeSb = 0;
374   int nDecoderSubbands;
375   SHORT L = 0; /* L: downsampling factor */
376   int offset = 0;
377   FIXP_DBL *audioReal = NULL, *audioImag = NULL;
378 
379   if (hGainDec->delayMode == DM_REGULAR_DELAY) {
380     offset = hGainDec->frameSize;
381   }
382 
383   if ((delaySamples + offset) >
384       (NUM_LNB_FRAMES - 2) *
385           hGainDec->frameSize) /* if delaySamples is too big, NUM_LNB_FRAMES
386                                   should be increased */
387     return DE_NOT_OK;
388 
389   switch (subbandDomainMode) {
390 #if ((1024 / 256) >= (4096 / SUBBAND_DOWNSAMPLING_FACTOR_QMF64))
391     case SDM_QMF64:
392       nDecoderSubbands = SUBBAND_NUM_BANDS_QMF64;
393       L = SUBBAND_DOWNSAMPLING_FACTOR_QMF64;
394       /* analysisDelay = SUBBAND_ANALYSIS_DELAY_QMF64; */
395       break;
396     case SDM_QMF71:
397       nDecoderSubbands = SUBBAND_NUM_BANDS_QMF71;
398       L = SUBBAND_DOWNSAMPLING_FACTOR_QMF71;
399       /* analysisDelay = SUBBAND_ANALYSIS_DELAY_QMF71; */
400       break;
401 #else
402     case SDM_QMF64:
403     case SDM_QMF71:
404       /* QMF domain processing is not supported. */
405       return DE_NOT_OK;
406 #endif
407     case SDM_STFT256:
408       nDecoderSubbands = SUBBAND_NUM_BANDS_STFT256;
409       L = SUBBAND_DOWNSAMPLING_FACTOR_STFT256;
410       /* analysisDelay = SUBBAND_ANALYSIS_DELAY_STFT256; */
411       break;
412     default:
413       return DE_NOT_OK;
414   }
415 
416   /* frameSizeSb = hGainDec->frameSize/L; */ /* L is a power of 2 */
417   frameSizeSb =
418       hGainDec->frameSize >> (15 - fixnormz_S(L)); /* timeslots per frame */
419 
420   if ((processSingleTimeslot < 0) || (processSingleTimeslot >= frameSizeSb)) {
421     m_start = 0;
422     m_stop = frameSizeSb;
423   } else {
424     m_start = processSingleTimeslot;
425     m_stop = m_start + 1;
426   }
427 
428   err = _prepareLnbIndex(pActiveDrc, channelOffset, drcChannelOffset,
429                          numChannelsProcessed, lnbPointer);
430   if (err) return err;
431 
432   if (!pActiveDrc->subbandGainsReady) /* only for the first time per frame that
433                                          processDrcSubband is called */
434   {
435     /* write subbandGains */
436     for (g = 0; g < pInst->nDrcChannelGroups; g++) {
437       b = 0;
438       {
439         LINEAR_NODE_BUFFER* pLnb =
440             &(pLinearNodeBuffer[activeDrcOffset +
441                                 pActiveDrc->gainElementForGroup[g] + b]);
442         NODE_LIN nodePrevious;
443         int lnbPointerDiff;
444 
445         for (m = 0; m < frameSizeSb; m++) {
446           subbandGains[activeDrcOffset + g][b * frameSizeSb + m] =
447               FL2FXCONST_DBL(1.0f / (float)(1 << 7));
448         }
449 
450         lnbIx = lnbPointer - (NUM_LNB_FRAMES - 1);
451         while (lnbIx < 0) lnbIx += NUM_LNB_FRAMES;
452 
453         /* Loop over all node buffers in linearNodeBuffer.
454            All nodes which are not relevant for the current frame are sorted out
455            inside _processNodeSegments. */
456         for (i = 0; i < NUM_LNB_FRAMES - 1; i++) {
457           /* Prepare previous node */
458           nodePrevious = pLnb->linearNode[lnbIx][pLnb->nNodes[lnbIx] - 1];
459           nodePrevious.time -= hGainDec->frameSize;
460 
461           lnbIx++;
462           if (lnbIx >= NUM_LNB_FRAMES) lnbIx = 0;
463 
464           /* number of frames of offset with respect to lnbPointer */
465           lnbPointerDiff = i - (NUM_LNB_FRAMES - 2);
466 
467           err = _processNodeSegments(
468               hGainDec->frameSize, pLnb->gainInterpolationType,
469               pLnb->nNodes[lnbIx], pLnb->linearNode[lnbIx],
470               lnbPointerDiff * hGainDec->frameSize + delaySamples + offset -
471                   (L - 1) / 2,
472               L, nodePrevious, FL2FXCONST_DBL(1.0f / (float)(1 << 8)),
473               &(subbandGains[activeDrcOffset + g][b * frameSizeSb]));
474           if (err) return err;
475         }
476       }
477     }
478     pActiveDrc->subbandGainsReady = 1;
479   }
480 
481   for (c = channelOffset; c < channelOffset + numChannelsProcessed; c++) {
482     FIXP_DBL* thisSubbandGainsBuffer;
483     if (pInst->drcSetId > 0)
484       g = pActiveDrc->channelGroupForChannel[c + drcChannelOffset];
485     else
486       g = -1;
487 
488     audioReal = deinterleavedAudioReal[signalIndex];
489     if (subbandDomainMode != SDM_STFT256) {
490       audioImag = deinterleavedAudioImag[signalIndex];
491     }
492 
493     if ((g >= 0) && !pActiveDrc->channelGroupIsParametricDrc[g]) {
494       thisSubbandGainsBuffer = subbandGains[activeDrcOffset + g];
495     } else {
496       thisSubbandGainsBuffer = dummySubbandGains;
497     }
498 
499     for (m = m_start; m < m_stop; m++) {
500       INT n_min = 8;
501       { /* single-band DRC */
502         gainSb = thisSubbandGainsBuffer[m];
503         if (activeDrcIndex == hGainDec->channelGainActiveDrcIndex)
504           gainSb = SATURATE_LEFT_SHIFT(
505               fMultDiv2(gainSb, hGainDec->channelGain[c]), 9, DFRACT_BITS);
506         /* normalize gainSb for keeping signal precision */
507         n_min = fMin(CntLeadingZeros(gainSb) - 1, n_min);
508         gainSb <<= n_min;
509         n_min = 8 - n_min;
510         if (subbandDomainMode ==
511             SDM_STFT256) { /* For STFT filterbank, real and imaginary parts are
512                               interleaved. */
513           for (s = 0; s < nDecoderSubbands; s++) {
514             *audioReal = fMultDiv2(*audioReal, gainSb) << n_min;
515             audioReal++;
516             *audioReal = fMultDiv2(*audioReal, gainSb) << n_min;
517             audioReal++;
518           }
519         } else {
520           for (s = 0; s < nDecoderSubbands; s++) {
521             *audioReal = fMultDiv2(*audioReal, gainSb) << n_min;
522             audioReal++;
523             *audioImag = fMultDiv2(*audioImag, gainSb) << n_min;
524             audioImag++;
525           }
526         }
527       }
528     }
529     signalIndex++;
530   }
531   return DE_OK;
532 }
533