• 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 /**************************** AAC encoder library ******************************
96 
97    Author(s):   M. Lohwasser
98 
99    Description: pns.c
100 
101 *******************************************************************************/
102 
103 #include "aacenc_pns.h"
104 
105 #include "psy_data.h"
106 #include "pnsparam.h"
107 #include "noisedet.h"
108 #include "bit_cnt.h"
109 #include "interface.h"
110 
111 /* minCorrelationEnergy = (1.0e-10f)^2 ~ 2^-67 = 2^-47 * 2^-20 */
112 static const FIXP_DBL minCorrelationEnergy =
113     FL2FXCONST_DBL(0.0); /* FL2FXCONST_DBL((float)FDKpow(2.0,-47)); */
114 /* noiseCorrelationThresh = 0.6^2 */
115 static const FIXP_DBL noiseCorrelationThresh = FL2FXCONST_DBL(0.36);
116 
117 static void FDKaacEnc_FDKaacEnc_noiseDetection(
118     PNS_CONFIG *pnsConf, PNS_DATA *pnsData, const INT sfbActive,
119     const INT *sfbOffset, INT tnsOrder, INT tnsPredictionGain, INT tnsActive,
120     FIXP_DBL *mdctSpectrum, INT *sfbMaxScaleSpec, FIXP_SGL *sfbtonality);
121 
122 static void FDKaacEnc_CalcNoiseNrgs(const INT sfbActive, INT *pnsFlag,
123                                     FIXP_DBL *sfbEnergyLdData, INT *noiseNrg);
124 
125 /*****************************************************************************
126 
127     functionname: initPnsConfiguration
128     description:  fill pnsConf with pns parameters
129     returns:      error status
130     input:        PNS Config struct (modified)
131                   bitrate, samplerate, usePns,
132                   number of sfb's, pointer to sfb offset
133     output:       error code
134 
135 *****************************************************************************/
136 
FDKaacEnc_InitPnsConfiguration(PNS_CONFIG * pnsConf,INT bitRate,INT sampleRate,INT usePns,INT sfbCnt,const INT * sfbOffset,const INT numChan,const INT isLC)137 AAC_ENCODER_ERROR FDKaacEnc_InitPnsConfiguration(
138     PNS_CONFIG *pnsConf, INT bitRate, INT sampleRate, INT usePns, INT sfbCnt,
139     const INT *sfbOffset, const INT numChan, const INT isLC) {
140   AAC_ENCODER_ERROR ErrorStatus;
141 
142   /* init noise detection */
143   ErrorStatus = FDKaacEnc_GetPnsParam(&pnsConf->np, bitRate, sampleRate, sfbCnt,
144                                       sfbOffset, &usePns, numChan, isLC);
145   if (ErrorStatus != AAC_ENC_OK) return ErrorStatus;
146 
147   pnsConf->minCorrelationEnergy = minCorrelationEnergy;
148   pnsConf->noiseCorrelationThresh = noiseCorrelationThresh;
149 
150   pnsConf->usePns = usePns;
151 
152   return AAC_ENC_OK;
153 }
154 
155 /*****************************************************************************
156 
157     functionname: FDKaacEnc_PnsDetect
158     description:  do decision, if PNS shall used or not
159     returns:
160     input:        pns config structure
161                   pns data structure (modified),
162                   lastWindowSequence (long or short blocks)
163                   sfbActive
164                   pointer to Sfb Energy, Threshold, Offset
165                   pointer to mdct Spectrum
166                   length of each group
167                   pointer to tonality calculated in chaosmeasure
168                   tns order and prediction gain
169                   calculated noiseNrg at active PNS
170     output:       pnsFlag in pns data structure
171 
172 *****************************************************************************/
FDKaacEnc_PnsDetect(PNS_CONFIG * pnsConf,PNS_DATA * pnsData,const INT lastWindowSequence,const INT sfbActive,const INT maxSfbPerGroup,FIXP_DBL * sfbThresholdLdData,const INT * sfbOffset,FIXP_DBL * mdctSpectrum,INT * sfbMaxScaleSpec,FIXP_SGL * sfbtonality,INT tnsOrder,INT tnsPredictionGain,INT tnsActive,FIXP_DBL * sfbEnergyLdData,INT * noiseNrg)173 void FDKaacEnc_PnsDetect(PNS_CONFIG *pnsConf, PNS_DATA *pnsData,
174                          const INT lastWindowSequence, const INT sfbActive,
175                          const INT maxSfbPerGroup, FIXP_DBL *sfbThresholdLdData,
176                          const INT *sfbOffset, FIXP_DBL *mdctSpectrum,
177                          INT *sfbMaxScaleSpec, FIXP_SGL *sfbtonality,
178                          INT tnsOrder, INT tnsPredictionGain, INT tnsActive,
179                          FIXP_DBL *sfbEnergyLdData, INT *noiseNrg)
180 
181 {
182   int sfb;
183   int startNoiseSfb;
184 
185   /* Reset pns info. */
186   FDKmemclear(pnsData->pnsFlag, sizeof(pnsData->pnsFlag));
187   for (sfb = 0; sfb < MAX_GROUPED_SFB; sfb++) {
188     noiseNrg[sfb] = NO_NOISE_PNS;
189   }
190 
191   /* Disable PNS and skip detection in certain cases. */
192   if (pnsConf->usePns == 0) {
193     return;
194   } else {
195     /* AAC - LC core encoder */
196     if ((pnsConf->np.detectionAlgorithmFlags & IS_LOW_COMPLEXITY) &&
197         (lastWindowSequence == SHORT_WINDOW)) {
198       return;
199     }
200     /* AAC - (E)LD core encoder */
201     if (!(pnsConf->np.detectionAlgorithmFlags & IS_LOW_COMPLEXITY) &&
202         (pnsConf->np.detectionAlgorithmFlags & JUST_LONG_WINDOW) &&
203         (lastWindowSequence != LONG_WINDOW)) {
204       return;
205     }
206   }
207 
208   /*
209     call noise detection
210   */
211   FDKaacEnc_FDKaacEnc_noiseDetection(
212       pnsConf, pnsData, sfbActive, sfbOffset, tnsOrder, tnsPredictionGain,
213       tnsActive, mdctSpectrum, sfbMaxScaleSpec, sfbtonality);
214 
215   /* set startNoiseSfb (long) */
216   startNoiseSfb = pnsConf->np.startSfb;
217 
218   /* Set noise substitution status */
219   for (sfb = 0; sfb < sfbActive; sfb++) {
220     /* No PNS below startNoiseSfb */
221     if (sfb < startNoiseSfb) {
222       pnsData->pnsFlag[sfb] = 0;
223       continue;
224     }
225 
226     /*
227       do noise substitution if
228       fuzzy measure is high enough
229       sfb freq > minimum sfb freq
230       signal in coder band is not masked
231     */
232 
233     if ((pnsData->noiseFuzzyMeasure[sfb] > FL2FXCONST_SGL(0.5)) &&
234         ((sfbThresholdLdData[sfb] +
235           FL2FXCONST_DBL(0.5849625f /
236                          64.0f)) /* thr * 1.5 = thrLd +ld(1.5)/64 */
237          < sfbEnergyLdData[sfb])) {
238       /*
239         mark in psyout flag array that we will code
240         this band with PNS
241       */
242       pnsData->pnsFlag[sfb] = 1; /* PNS_ON */
243     } else {
244       pnsData->pnsFlag[sfb] = 0; /* PNS_OFF */
245     }
246 
247     /* no PNS if LTP is active */
248   }
249 
250   /* avoid PNS holes */
251   if ((pnsData->noiseFuzzyMeasure[0] > FL2FXCONST_SGL(0.5f)) &&
252       (pnsData->pnsFlag[1])) {
253     pnsData->pnsFlag[0] = 1;
254   }
255 
256   for (sfb = 1; sfb < maxSfbPerGroup - 1; sfb++) {
257     if ((pnsData->noiseFuzzyMeasure[sfb] > pnsConf->np.gapFillThr) &&
258         (pnsData->pnsFlag[sfb - 1]) && (pnsData->pnsFlag[sfb + 1])) {
259       pnsData->pnsFlag[sfb] = 1;
260     }
261   }
262 
263   if (maxSfbPerGroup > 0) {
264     /* avoid PNS hole */
265     if ((pnsData->noiseFuzzyMeasure[maxSfbPerGroup - 1] >
266          pnsConf->np.gapFillThr) &&
267         (pnsData->pnsFlag[maxSfbPerGroup - 2])) {
268       pnsData->pnsFlag[maxSfbPerGroup - 1] = 1;
269     }
270     /* avoid single PNS band */
271     if (pnsData->pnsFlag[maxSfbPerGroup - 2] == 0) {
272       pnsData->pnsFlag[maxSfbPerGroup - 1] = 0;
273     }
274   }
275 
276   /* avoid single PNS bands */
277   if (pnsData->pnsFlag[1] == 0) {
278     pnsData->pnsFlag[0] = 0;
279   }
280 
281   for (sfb = 1; sfb < maxSfbPerGroup - 1; sfb++) {
282     if ((pnsData->pnsFlag[sfb - 1] == 0) && (pnsData->pnsFlag[sfb + 1] == 0)) {
283       pnsData->pnsFlag[sfb] = 0;
284     }
285   }
286 
287   /*
288     calculate noiseNrg's
289   */
290   FDKaacEnc_CalcNoiseNrgs(sfbActive, pnsData->pnsFlag, sfbEnergyLdData,
291                           noiseNrg);
292 }
293 
294 /*****************************************************************************
295 
296     functionname:FDKaacEnc_FDKaacEnc_noiseDetection
297     description: wrapper for noisedet.c
298     returns:
299     input:       pns config structure
300                  pns data structure (modified),
301                  sfbActive
302                  tns order and prediction gain
303                  pointer to mdct Spectrumand Sfb Energy
304                  pointer to Sfb tonality
305     output:      noiseFuzzyMeasure in structure pnsData
306                  flags tonal / nontonal
307 
308 *****************************************************************************/
FDKaacEnc_FDKaacEnc_noiseDetection(PNS_CONFIG * pnsConf,PNS_DATA * pnsData,const INT sfbActive,const INT * sfbOffset,int tnsOrder,INT tnsPredictionGain,INT tnsActive,FIXP_DBL * mdctSpectrum,INT * sfbMaxScaleSpec,FIXP_SGL * sfbtonality)309 static void FDKaacEnc_FDKaacEnc_noiseDetection(
310     PNS_CONFIG *pnsConf, PNS_DATA *pnsData, const INT sfbActive,
311     const INT *sfbOffset, int tnsOrder, INT tnsPredictionGain, INT tnsActive,
312     FIXP_DBL *mdctSpectrum, INT *sfbMaxScaleSpec, FIXP_SGL *sfbtonality) {
313   INT condition = TRUE;
314   if (!(pnsConf->np.detectionAlgorithmFlags & IS_LOW_COMPLEXITY)) {
315     condition = (tnsOrder > 3);
316   }
317   /*
318   no PNS if heavy TNS activity
319   clear pnsData->noiseFuzzyMeasure
320   */
321   if ((pnsConf->np.detectionAlgorithmFlags & USE_TNS_GAIN_THR) &&
322       (tnsPredictionGain >= pnsConf->np.tnsGainThreshold) && condition &&
323       !((pnsConf->np.detectionAlgorithmFlags & USE_TNS_PNS) &&
324         (tnsPredictionGain >= pnsConf->np.tnsPNSGainThreshold) &&
325         (tnsActive))) {
326     /* clear all noiseFuzzyMeasure */
327     FDKmemclear(pnsData->noiseFuzzyMeasure, sfbActive * sizeof(FIXP_SGL));
328   } else {
329     /*
330     call noise detection, output in pnsData->noiseFuzzyMeasure,
331     use real mdct spectral data
332     */
333     FDKaacEnc_noiseDetect(mdctSpectrum, sfbMaxScaleSpec, sfbActive, sfbOffset,
334                           pnsData->noiseFuzzyMeasure, &pnsConf->np,
335                           sfbtonality);
336   }
337 }
338 
339 /*****************************************************************************
340 
341     functionname:FDKaacEnc_CalcNoiseNrgs
342     description: Calculate the NoiseNrg's
343     returns:
344     input:       sfbActive
345                  if pnsFlag calculate NoiseNrg
346                  pointer to sfbEnergy and groupLen
347                  pointer to noiseNrg (modified)
348     output:      noiseNrg's in pnsFlaged sfb's
349 
350 *****************************************************************************/
351 
FDKaacEnc_CalcNoiseNrgs(const INT sfbActive,INT * RESTRICT pnsFlag,FIXP_DBL * RESTRICT sfbEnergyLdData,INT * RESTRICT noiseNrg)352 static void FDKaacEnc_CalcNoiseNrgs(const INT sfbActive, INT *RESTRICT pnsFlag,
353                                     FIXP_DBL *RESTRICT sfbEnergyLdData,
354                                     INT *RESTRICT noiseNrg) {
355   int sfb;
356   INT tmp = (-LOG_NORM_PCM) << 2;
357 
358   for (sfb = 0; sfb < sfbActive; sfb++) {
359     if (pnsFlag[sfb]) {
360       INT nrg = (-sfbEnergyLdData[sfb] + FL2FXCONST_DBL(0.5f / 64.0f)) >>
361                 (DFRACT_BITS - 1 - 7);
362       noiseNrg[sfb] = tmp - nrg;
363     }
364   }
365 }
366 
367 /*****************************************************************************
368 
369     functionname:FDKaacEnc_CodePnsChannel
370     description: Execute pns decission
371     returns:
372     input:       sfbActive
373                  pns config structure
374                  use PNS if pnsFlag
375                  pointer to Sfb Energy, noiseNrg, Threshold
376     output:      set sfbThreshold high to code pe with 0,
377                  noiseNrg marks flag for pns coding
378 
379 *****************************************************************************/
380 
FDKaacEnc_CodePnsChannel(const INT sfbActive,PNS_CONFIG * pnsConf,INT * RESTRICT pnsFlag,FIXP_DBL * RESTRICT sfbEnergyLdData,INT * RESTRICT noiseNrg,FIXP_DBL * RESTRICT sfbThresholdLdData)381 void FDKaacEnc_CodePnsChannel(const INT sfbActive, PNS_CONFIG *pnsConf,
382                               INT *RESTRICT pnsFlag,
383                               FIXP_DBL *RESTRICT sfbEnergyLdData,
384                               INT *RESTRICT noiseNrg,
385                               FIXP_DBL *RESTRICT sfbThresholdLdData) {
386   INT sfb;
387   INT lastiNoiseEnergy = 0;
388   INT firstPNSband = 1; /* TRUE for first PNS-coded band */
389 
390   /* no PNS */
391   if (!pnsConf->usePns) {
392     for (sfb = 0; sfb < sfbActive; sfb++) {
393       /* no PNS coding */
394       noiseNrg[sfb] = NO_NOISE_PNS;
395     }
396     return;
397   }
398 
399   /* code PNS */
400   for (sfb = 0; sfb < sfbActive; sfb++) {
401     if (pnsFlag[sfb]) {
402       /* high sfbThreshold causes pe = 0 */
403       if (noiseNrg[sfb] != NO_NOISE_PNS)
404         sfbThresholdLdData[sfb] =
405             sfbEnergyLdData[sfb] + FL2FXCONST_DBL(1.0f / LD_DATA_SCALING);
406 
407       /* set noiseNrg in valid region */
408       if (!firstPNSband) {
409         INT deltaiNoiseEnergy = noiseNrg[sfb] - lastiNoiseEnergy;
410 
411         if (deltaiNoiseEnergy > CODE_BOOK_PNS_LAV)
412           noiseNrg[sfb] -= deltaiNoiseEnergy - CODE_BOOK_PNS_LAV;
413         else if (deltaiNoiseEnergy < -CODE_BOOK_PNS_LAV)
414           noiseNrg[sfb] -= deltaiNoiseEnergy + CODE_BOOK_PNS_LAV;
415       } else {
416         firstPNSband = 0;
417       }
418       lastiNoiseEnergy = noiseNrg[sfb];
419     } else {
420       /* no PNS coding */
421       noiseNrg[sfb] = NO_NOISE_PNS;
422     }
423   }
424 }
425 
426 /*****************************************************************************
427 
428     functionname:FDKaacEnc_PreProcessPnsChannelPair
429     description: Calculate the correlation of noise in a channel pair
430 
431     returns:
432     input:       sfbActive
433                  pointer to sfb energies left, right and mid channel
434                  pns config structure
435                  pns data structure left and right (modified)
436 
437     output:      noiseEnergyCorrelation in pns data structure
438 
439 *****************************************************************************/
440 
FDKaacEnc_PreProcessPnsChannelPair(const INT sfbActive,FIXP_DBL * RESTRICT sfbEnergyLeft,FIXP_DBL * RESTRICT sfbEnergyRight,FIXP_DBL * RESTRICT sfbEnergyLeftLD,FIXP_DBL * RESTRICT sfbEnergyRightLD,FIXP_DBL * RESTRICT sfbEnergyMid,PNS_CONFIG * RESTRICT pnsConf,PNS_DATA * pnsDataLeft,PNS_DATA * pnsDataRight)441 void FDKaacEnc_PreProcessPnsChannelPair(
442     const INT sfbActive, FIXP_DBL *RESTRICT sfbEnergyLeft,
443     FIXP_DBL *RESTRICT sfbEnergyRight, FIXP_DBL *RESTRICT sfbEnergyLeftLD,
444     FIXP_DBL *RESTRICT sfbEnergyRightLD, FIXP_DBL *RESTRICT sfbEnergyMid,
445     PNS_CONFIG *RESTRICT pnsConf, PNS_DATA *pnsDataLeft,
446     PNS_DATA *pnsDataRight) {
447   INT sfb;
448   FIXP_DBL ccf;
449 
450   if (!pnsConf->usePns) return;
451 
452   FIXP_DBL *RESTRICT pNoiseEnergyCorrelationL =
453       pnsDataLeft->noiseEnergyCorrelation;
454   FIXP_DBL *RESTRICT pNoiseEnergyCorrelationR =
455       pnsDataRight->noiseEnergyCorrelation;
456 
457   for (sfb = 0; sfb < sfbActive; sfb++) {
458     FIXP_DBL quot = (sfbEnergyLeftLD[sfb] >> 1) + (sfbEnergyRightLD[sfb] >> 1);
459 
460     if (quot < FL2FXCONST_DBL(-32.0f / (float)LD_DATA_SCALING))
461       ccf = FL2FXCONST_DBL(0.0f);
462     else {
463       FIXP_DBL accu =
464           sfbEnergyMid[sfb] -
465           (((sfbEnergyLeft[sfb] >> 1) + (sfbEnergyRight[sfb] >> 1)) >> 1);
466       INT sign = (accu < FL2FXCONST_DBL(0.0f)) ? 1 : 0;
467       accu = fixp_abs(accu);
468 
469       ccf = CalcLdData(accu) +
470             FL2FXCONST_DBL((float)1.0f / (float)LD_DATA_SCALING) -
471             quot; /* ld(accu*2) = ld(accu) + 1 */
472       ccf = (ccf >= FL2FXCONST_DBL(0.0))
473                 ? ((FIXP_DBL)MAXVAL_DBL)
474                 : (sign) ? -CalcInvLdData(ccf) : CalcInvLdData(ccf);
475     }
476 
477     pNoiseEnergyCorrelationL[sfb] = ccf;
478     pNoiseEnergyCorrelationR[sfb] = ccf;
479   }
480 }
481 
482 /*****************************************************************************
483 
484     functionname:FDKaacEnc_PostProcessPnsChannelPair
485     description: if PNS used at left and right channel,
486                  use msMask to flag correlation
487     returns:
488     input:       sfbActive
489                  pns config structure
490                  pns data structure left and right (modified)
491                  pointer to msMask, flags correlation by pns coding (modified)
492                  Digest of MS coding
493     output:      pnsFlag in pns data structure,
494                  msFlag in msMask (flags correlation)
495 
496 *****************************************************************************/
497 
FDKaacEnc_PostProcessPnsChannelPair(const INT sfbActive,PNS_CONFIG * pnsConf,PNS_DATA * pnsDataLeft,PNS_DATA * pnsDataRight,INT * RESTRICT msMask,INT * msDigest)498 void FDKaacEnc_PostProcessPnsChannelPair(const INT sfbActive,
499                                          PNS_CONFIG *pnsConf,
500                                          PNS_DATA *pnsDataLeft,
501                                          PNS_DATA *pnsDataRight,
502                                          INT *RESTRICT msMask, INT *msDigest) {
503   INT sfb;
504 
505   if (!pnsConf->usePns) return;
506 
507   for (sfb = 0; sfb < sfbActive; sfb++) {
508     /*
509       MS post processing
510     */
511     if (msMask[sfb]) {
512       if ((pnsDataLeft->pnsFlag[sfb]) && (pnsDataRight->pnsFlag[sfb])) {
513         /* AAC only: Standard */
514         /* do this to avoid ms flags in layers that should not have it */
515         if (pnsDataLeft->noiseEnergyCorrelation[sfb] <=
516             pnsConf->noiseCorrelationThresh) {
517           msMask[sfb] = 0;
518           *msDigest = MS_SOME;
519         }
520       } else {
521         /*
522           No PNS coding
523         */
524         pnsDataLeft->pnsFlag[sfb] = 0;
525         pnsDataRight->pnsFlag[sfb] = 0;
526       }
527     }
528 
529     /*
530       Use MS flag to signal noise correlation if
531       pns is active in both channels
532     */
533     if ((pnsDataLeft->pnsFlag[sfb]) && (pnsDataRight->pnsFlag[sfb])) {
534       if (pnsDataLeft->noiseEnergyCorrelation[sfb] >
535           pnsConf->noiseCorrelationThresh) {
536         msMask[sfb] = 1;
537         *msDigest = MS_SOME;
538       }
539     }
540   }
541 }
542