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

/**************************** AAC decoder library ******************************

   Author(s):   Josef Hoepfl

   Description: joint stereo processing

*******************************************************************************/

#include "stereo.h"

#include "aac_rom.h"
#include "FDK_bitstream.h"
#include "channelinfo.h"
#include "FDK_audio.h"

enum { L = 0, R = 1 };

#include "block.h"

int CJointStereo_Read(HANDLE_FDK_BITSTREAM bs,
                      CJointStereoData *pJointStereoData,
                      const int windowGroups,
                      const int scaleFactorBandsTransmitted,
                      const int max_sfb_ste_clear,
                      CJointStereoPersistentData *pJointStereoPersistentData,
                      CCplxPredictionData *cplxPredictionData,
                      int cplxPredictionActiv, int scaleFactorBandsTotal,
                      int windowSequence, const UINT flags) {
  int group, band;

  pJointStereoData->MsMaskPresent = (UCHAR)FDKreadBits(bs, 2);

  FDKmemclear(pJointStereoData->MsUsed,
              scaleFactorBandsTransmitted * sizeof(UCHAR));

  pJointStereoData->cplx_pred_flag = 0;
  if (cplxPredictionActiv) {
    cplxPredictionData->pred_dir = 0;
    cplxPredictionData->complex_coef = 0;
    cplxPredictionData->use_prev_frame = 0;
    cplxPredictionData->igf_pred_dir = 0;
  }

  switch (pJointStereoData->MsMaskPresent) {
    case 0: /* no M/S */
      /* all flags are already cleared */
      break;

    case 1: /* read ms_used */
      for (group = 0; group < windowGroups; group++) {
        for (band = 0; band < scaleFactorBandsTransmitted; band++) {
          pJointStereoData->MsUsed[band] |= (FDKreadBits(bs, 1) << group);
        }
      }
      break;

    case 2: /* full spectrum M/S */
      for (band = 0; band < scaleFactorBandsTransmitted; band++) {
        pJointStereoData->MsUsed[band] = 255; /* set all flags to 1 */
      }
      break;

    case 3:
      /* M/S coding is disabled, complex stereo prediction is enabled */
      if (flags & (AC_USAC | AC_RSVD50 | AC_RSV603DA)) {
        if (cplxPredictionActiv) { /* 'if (stereoConfigIndex == 0)' */

          pJointStereoData->cplx_pred_flag = 1;

          /* cplx_pred_data()  cp. ISO/IEC FDIS 23003-3:2011(E)  Table 26 */
          int cplx_pred_all = 0; /* local use only */
          cplx_pred_all = FDKreadBits(bs, 1);

          if (cplx_pred_all) {
            for (group = 0; group < windowGroups; group++) {
              UCHAR groupmask = ((UCHAR)1 << group);
              for (band = 0; band < scaleFactorBandsTransmitted; band++) {
                pJointStereoData->MsUsed[band] |= groupmask;
              }
            }
          } else {
            for (group = 0; group < windowGroups; group++) {
              for (band = 0; band < scaleFactorBandsTransmitted;
                   band += SFB_PER_PRED_BAND) {
                pJointStereoData->MsUsed[band] |= (FDKreadBits(bs, 1) << group);
                if ((band + 1) < scaleFactorBandsTotal) {
                  pJointStereoData->MsUsed[band + 1] |=
                      (pJointStereoData->MsUsed[band] & ((UCHAR)1 << group));
                }
              }
            }
          }
        } else {
          return -1;
        }
      }
      break;
  }

  if (cplxPredictionActiv) {
    /* If all sfb are MS-ed then no complex prediction */
    if (pJointStereoData->MsMaskPresent == 3) {
      if (pJointStereoData->cplx_pred_flag) {
        int delta_code_time = 0;

        /* set pointer to Huffman codebooks */
        const CodeBookDescription *hcb = &AACcodeBookDescriptionTable[BOOKSCL];
        /* set predictors to zero in case of a transition from long to short
         * window sequences and vice versa */
        if (((windowSequence == BLOCK_SHORT) &&
             (pJointStereoPersistentData->winSeqPrev != BLOCK_SHORT)) ||
            ((pJointStereoPersistentData->winSeqPrev == BLOCK_SHORT) &&
             (windowSequence != BLOCK_SHORT))) {
          FDKmemclear(pJointStereoPersistentData->alpha_q_re_prev,
                      JointStereoMaximumGroups * JointStereoMaximumBands *
                          sizeof(SHORT));
          FDKmemclear(pJointStereoPersistentData->alpha_q_im_prev,
                      JointStereoMaximumGroups * JointStereoMaximumBands *
                          sizeof(SHORT));
        }
        {
          FDKmemclear(cplxPredictionData->alpha_q_re,
                      JointStereoMaximumGroups * JointStereoMaximumBands *
                          sizeof(SHORT));
          FDKmemclear(cplxPredictionData->alpha_q_im,
                      JointStereoMaximumGroups * JointStereoMaximumBands *
                          sizeof(SHORT));
        }

        /* 0 = mid->side prediction, 1 = side->mid prediction */
        cplxPredictionData->pred_dir = FDKreadBits(bs, 1);
        cplxPredictionData->complex_coef = FDKreadBits(bs, 1);

        if (cplxPredictionData->complex_coef) {
          if (flags & AC_INDEP) {
            cplxPredictionData->use_prev_frame = 0;
          } else {
            cplxPredictionData->use_prev_frame = FDKreadBits(bs, 1);
          }
        }

        if (flags & AC_INDEP) {
          delta_code_time = 0;
        } else {
          delta_code_time = FDKreadBits(bs, 1);
        }

        {
          int last_alpha_q_re = 0, last_alpha_q_im = 0;

          for (group = 0; group < windowGroups; group++) {
            for (band = 0; band < scaleFactorBandsTransmitted;
                 band += SFB_PER_PRED_BAND) {
              if (delta_code_time == 1) {
                if (group > 0) {
                  last_alpha_q_re =
                      cplxPredictionData->alpha_q_re[group - 1][band];
                  last_alpha_q_im =
                      cplxPredictionData->alpha_q_im[group - 1][band];
                } else if ((windowSequence == BLOCK_SHORT) &&
                           (pJointStereoPersistentData->winSeqPrev ==
                            BLOCK_SHORT)) {
                  /* Included for error-robustness */
                  if (pJointStereoPersistentData->winGroupsPrev == 0) return -1;

                  last_alpha_q_re =
                      pJointStereoPersistentData->alpha_q_re_prev
                          [pJointStereoPersistentData->winGroupsPrev - 1][band];
                  last_alpha_q_im =
                      pJointStereoPersistentData->alpha_q_im_prev
                          [pJointStereoPersistentData->winGroupsPrev - 1][band];
                } else {
                  last_alpha_q_re =
                      pJointStereoPersistentData->alpha_q_re_prev[group][band];
                  last_alpha_q_im =
                      pJointStereoPersistentData->alpha_q_im_prev[group][band];
                }

              } else {
                if (band > 0) {
                  last_alpha_q_re =
                      cplxPredictionData->alpha_q_re[group][band - 1];
                  last_alpha_q_im =
                      cplxPredictionData->alpha_q_im[group][band - 1];
                } else {
                  last_alpha_q_re = 0;
                  last_alpha_q_im = 0;
                }

              } /* if (delta_code_time == 1) */

              if (pJointStereoData->MsUsed[band] & ((UCHAR)1 << group)) {
                int dpcm_alpha_re, dpcm_alpha_im;

                dpcm_alpha_re = CBlock_DecodeHuffmanWord(bs, hcb);
                dpcm_alpha_re -= 60;
                dpcm_alpha_re *= -1;

                cplxPredictionData->alpha_q_re[group][band] =
                    dpcm_alpha_re + last_alpha_q_re;

                if (cplxPredictionData->complex_coef) {
                  dpcm_alpha_im = CBlock_DecodeHuffmanWord(bs, hcb);
                  dpcm_alpha_im -= 60;
                  dpcm_alpha_im *= -1;

                  cplxPredictionData->alpha_q_im[group][band] =
                      dpcm_alpha_im + last_alpha_q_im;
                } else {
                  cplxPredictionData->alpha_q_im[group][band] = 0;
                }

              } else {
                cplxPredictionData->alpha_q_re[group][band] = 0;
                cplxPredictionData->alpha_q_im[group][band] = 0;
              } /* if (pJointStereoData->MsUsed[band] & ((UCHAR)1 << group)) */

              if ((band + 1) <
                  scaleFactorBandsTransmitted) { /* <= this should be the
                                                    correct way (cp.
                                                    ISO_IEC_FDIS_23003-0(E) */
                /*    7.7.2.3.2 Decoding of prediction coefficients) */
                cplxPredictionData->alpha_q_re[group][band + 1] =
                    cplxPredictionData->alpha_q_re[group][band];
                cplxPredictionData->alpha_q_im[group][band + 1] =
                    cplxPredictionData->alpha_q_im[group][band];
              } /* if ((band+1)<scaleFactorBandsTotal) */

              pJointStereoPersistentData->alpha_q_re_prev[group][band] =
                  cplxPredictionData->alpha_q_re[group][band];
              pJointStereoPersistentData->alpha_q_im_prev[group][band] =
                  cplxPredictionData->alpha_q_im[group][band];
            }

            for (band = scaleFactorBandsTransmitted; band < max_sfb_ste_clear;
                 band++) {
              cplxPredictionData->alpha_q_re[group][band] = 0;
              cplxPredictionData->alpha_q_im[group][band] = 0;
              pJointStereoPersistentData->alpha_q_re_prev[group][band] = 0;
              pJointStereoPersistentData->alpha_q_im_prev[group][band] = 0;
            }
          }
        }
      }
    } else {
      for (group = 0; group < windowGroups; group++) {
        for (band = 0; band < max_sfb_ste_clear; band++) {
          pJointStereoPersistentData->alpha_q_re_prev[group][band] = 0;
          pJointStereoPersistentData->alpha_q_im_prev[group][band] = 0;
        }
      }
    }

    pJointStereoPersistentData->winGroupsPrev = windowGroups;
  }

  return 0;
}

static void CJointStereo_filterAndAdd(
    FIXP_DBL *in, int len, int windowLen, const FIXP_FILT *coeff, FIXP_DBL *out,
    UCHAR isCurrent /* output values with even index get a
                       positve addon (=1) or a negative addon
                       (=0) */
) {
  int i, j;

  int indices_1[] = {2, 1, 0, 1, 2, 3};
  int indices_2[] = {1, 0, 0, 2, 3, 4};
  int indices_3[] = {0, 0, 1, 3, 4, 5};

  int subtr_1[] = {6, 5, 4, 2, 1, 1};
  int subtr_2[] = {5, 4, 3, 1, 1, 2};
  int subtr_3[] = {4, 3, 2, 1, 2, 3};

  if (isCurrent == 1) {
    /* exploit the symmetry of the table: coeff[6] = - coeff[0],
                                          coeff[5] = - coeff[1],
                                          coeff[4] = - coeff[2],
                                          coeff[3] = 0
    */

    for (i = 0; i < 3; i++) {
      out[0] -= (FIXP_DBL)fMultDiv2(coeff[i], in[indices_1[i]]) >> SR_FNA_OUT;
      out[0] +=
          (FIXP_DBL)fMultDiv2(coeff[i], in[indices_1[5 - i]]) >> SR_FNA_OUT;
    }

    for (i = 0; i < 3; i++) {
      out[1] -= (FIXP_DBL)fMultDiv2(coeff[i], in[indices_2[i]]) >> SR_FNA_OUT;
      out[1] +=
          (FIXP_DBL)fMultDiv2(coeff[i], in[indices_2[5 - i]]) >> SR_FNA_OUT;
    }

    for (i = 0; i < 3; i++) {
      out[2] -= (FIXP_DBL)fMultDiv2(coeff[i], in[indices_3[i]]) >> SR_FNA_OUT;
      out[2] +=
          (FIXP_DBL)fMultDiv2(coeff[i], in[indices_3[5 - i]]) >> SR_FNA_OUT;
    }

    for (j = 3; j < (len - 3); j++) {
      for (i = 0; i < 3; i++) {
        out[j] -= (FIXP_DBL)fMultDiv2(coeff[i], in[j - 3 + i]) >> SR_FNA_OUT;
        out[j] += (FIXP_DBL)fMultDiv2(coeff[i], in[j + 3 - i]) >> SR_FNA_OUT;
      }
    }

    for (i = 0; i < 3; i++) {
      out[len - 3] -=
          (FIXP_DBL)fMultDiv2(coeff[i], in[len - subtr_1[i]]) >> SR_FNA_OUT;
      out[len - 3] +=
          (FIXP_DBL)fMultDiv2(coeff[i], in[len - subtr_1[5 - i]]) >> SR_FNA_OUT;
    }

    for (i = 0; i < 3; i++) {
      out[len - 2] -=
          (FIXP_DBL)fMultDiv2(coeff[i], in[len - subtr_2[i]]) >> SR_FNA_OUT;
      out[len - 2] +=
          (FIXP_DBL)fMultDiv2(coeff[i], in[len - subtr_2[5 - i]]) >> SR_FNA_OUT;
    }

    for (i = 0; i < 3; i++) {
      out[len - 1] -=
          (FIXP_DBL)fMultDiv2(coeff[i], in[len - subtr_3[i]]) >> SR_FNA_OUT;
      out[len - 1] +=
          (FIXP_DBL)fMultDiv2(coeff[i], in[len - subtr_3[5 - i]]) >> SR_FNA_OUT;
    }

  } else {
    /* exploit the symmetry of the table: coeff[6] = coeff[0],
                                          coeff[5] = coeff[1],
                                          coeff[4] = coeff[2]
    */

    for (i = 0; i < 3; i++) {
      out[0] -= (FIXP_DBL)fMultDiv2(coeff[i], in[indices_1[i]] >> SR_FNA_OUT);
      out[0] -=
          (FIXP_DBL)fMultDiv2(coeff[i], in[indices_1[5 - i]] >> SR_FNA_OUT);
    }
    out[0] -= (FIXP_DBL)fMultDiv2(coeff[3], in[0] >> SR_FNA_OUT);

    for (i = 0; i < 3; i++) {
      out[1] += (FIXP_DBL)fMultDiv2(coeff[i], in[indices_2[i]] >> SR_FNA_OUT);
      out[1] +=
          (FIXP_DBL)fMultDiv2(coeff[i], in[indices_2[5 - i]] >> SR_FNA_OUT);
    }
    out[1] += (FIXP_DBL)fMultDiv2(coeff[3], in[1] >> SR_FNA_OUT);

    for (i = 0; i < 3; i++) {
      out[2] -= (FIXP_DBL)fMultDiv2(coeff[i], in[indices_3[i]] >> SR_FNA_OUT);
      out[2] -=
          (FIXP_DBL)fMultDiv2(coeff[i], in[indices_3[5 - i]] >> SR_FNA_OUT);
    }
    out[2] -= (FIXP_DBL)fMultDiv2(coeff[3], in[2] >> SR_FNA_OUT);

    for (j = 3; j < (len - 4); j++) {
      for (i = 0; i < 3; i++) {
        out[j] += (FIXP_DBL)fMultDiv2(coeff[i], in[j - 3 + i] >> SR_FNA_OUT);
        out[j] += (FIXP_DBL)fMultDiv2(coeff[i], in[j + 3 - i] >> SR_FNA_OUT);
      }
      out[j] += (FIXP_DBL)fMultDiv2(coeff[3], in[j] >> SR_FNA_OUT);

      j++;

      for (i = 0; i < 3; i++) {
        out[j] -= (FIXP_DBL)fMultDiv2(coeff[i], in[j - 3 + i] >> SR_FNA_OUT);
        out[j] -= (FIXP_DBL)fMultDiv2(coeff[i], in[j + 3 - i] >> SR_FNA_OUT);
      }
      out[j] -= (FIXP_DBL)fMultDiv2(coeff[3], in[j] >> SR_FNA_OUT);
    }

    for (i = 0; i < 3; i++) {
      out[len - 3] +=
          (FIXP_DBL)fMultDiv2(coeff[i], in[len - subtr_1[i]] >> SR_FNA_OUT);
      out[len - 3] +=
          (FIXP_DBL)fMultDiv2(coeff[i], in[len - subtr_1[5 - i]] >> SR_FNA_OUT);
    }
    out[len - 3] += (FIXP_DBL)fMultDiv2(coeff[3], in[len - 3] >> SR_FNA_OUT);

    for (i = 0; i < 3; i++) {
      out[len - 2] -=
          (FIXP_DBL)fMultDiv2(coeff[i], in[len - subtr_2[i]] >> SR_FNA_OUT);
      out[len - 2] -=
          (FIXP_DBL)fMultDiv2(coeff[i], in[len - subtr_2[5 - i]] >> SR_FNA_OUT);
    }
    out[len - 2] -= (FIXP_DBL)fMultDiv2(coeff[3], in[len - 2] >> SR_FNA_OUT);

    for (i = 0; i < 3; i++) {
      out[len - 1] +=
          (FIXP_DBL)fMultDiv2(coeff[i], in[len - subtr_3[i]] >> SR_FNA_OUT);
      out[len - 1] +=
          (FIXP_DBL)fMultDiv2(coeff[i], in[len - subtr_3[5 - i]] >> SR_FNA_OUT);
    }
    out[len - 1] += (FIXP_DBL)fMultDiv2(coeff[3], in[len - 1] >> SR_FNA_OUT);
  }
}

static inline void CJointStereo_GenerateMSOutput(FIXP_DBL *pSpecLCurrBand,
                                                 FIXP_DBL *pSpecRCurrBand,
                                                 UINT leftScale,
                                                 UINT rightScale,
                                                 UINT nSfbBands) {
  unsigned int i;

  FIXP_DBL leftCoefficient0;
  FIXP_DBL leftCoefficient1;
  FIXP_DBL leftCoefficient2;
  FIXP_DBL leftCoefficient3;

  FIXP_DBL rightCoefficient0;
  FIXP_DBL rightCoefficient1;
  FIXP_DBL rightCoefficient2;
  FIXP_DBL rightCoefficient3;

  for (i = nSfbBands; i > 0; i -= 4) {
    leftCoefficient0 = pSpecLCurrBand[i - 4];
    leftCoefficient1 = pSpecLCurrBand[i - 3];
    leftCoefficient2 = pSpecLCurrBand[i - 2];
    leftCoefficient3 = pSpecLCurrBand[i - 1];

    rightCoefficient0 = pSpecRCurrBand[i - 4];
    rightCoefficient1 = pSpecRCurrBand[i - 3];
    rightCoefficient2 = pSpecRCurrBand[i - 2];
    rightCoefficient3 = pSpecRCurrBand[i - 1];

    /* MS output generation */
    leftCoefficient0 >>= leftScale;
    leftCoefficient1 >>= leftScale;
    leftCoefficient2 >>= leftScale;
    leftCoefficient3 >>= leftScale;

    rightCoefficient0 >>= rightScale;
    rightCoefficient1 >>= rightScale;
    rightCoefficient2 >>= rightScale;
    rightCoefficient3 >>= rightScale;

    pSpecLCurrBand[i - 4] = leftCoefficient0 + rightCoefficient0;
    pSpecLCurrBand[i - 3] = leftCoefficient1 + rightCoefficient1;
    pSpecLCurrBand[i - 2] = leftCoefficient2 + rightCoefficient2;
    pSpecLCurrBand[i - 1] = leftCoefficient3 + rightCoefficient3;

    pSpecRCurrBand[i - 4] = leftCoefficient0 - rightCoefficient0;
    pSpecRCurrBand[i - 3] = leftCoefficient1 - rightCoefficient1;
    pSpecRCurrBand[i - 2] = leftCoefficient2 - rightCoefficient2;
    pSpecRCurrBand[i - 1] = leftCoefficient3 - rightCoefficient3;
  }
}

void CJointStereo_ApplyMS(
    CAacDecoderChannelInfo *pAacDecoderChannelInfo[2],
    CAacDecoderStaticChannelInfo *pAacDecoderStaticChannelInfo[2],
    FIXP_DBL *spectrumL, FIXP_DBL *spectrumR, SHORT *SFBleftScale,
    SHORT *SFBrightScale, SHORT *specScaleL, SHORT *specScaleR,
    const SHORT *pScaleFactorBandOffsets, const UCHAR *pWindowGroupLength,
    const int windowGroups, const int max_sfb_ste_outside,
    const int scaleFactorBandsTransmittedL,
    const int scaleFactorBandsTransmittedR, FIXP_DBL *store_dmx_re_prev,
    SHORT *store_dmx_re_prev_e, const int mainband_flag) {
  int window, group, band;
  UCHAR groupMask;
  CJointStereoData *pJointStereoData =
      &pAacDecoderChannelInfo[L]->pComData->jointStereoData;
  CCplxPredictionData *cplxPredictionData =
      pAacDecoderChannelInfo[L]->pComStaticData->cplxPredictionData;

  int max_sfb_ste =
      fMax(scaleFactorBandsTransmittedL, scaleFactorBandsTransmittedR);
  int min_sfb_ste =
      fMin(scaleFactorBandsTransmittedL, scaleFactorBandsTransmittedR);
  int scaleFactorBandsTransmitted = min_sfb_ste;

  if (pJointStereoData->cplx_pred_flag) {
    int windowLen, groupwin, frameMaxScale;
    CJointStereoPersistentData *pJointStereoPersistentData =
        &pAacDecoderStaticChannelInfo[L]
             ->pCpeStaticData->jointStereoPersistentData;
    FIXP_DBL *const staticSpectralCoeffsL =
        pAacDecoderStaticChannelInfo[L]
            ->pCpeStaticData->jointStereoPersistentData.spectralCoeffs[L];
    FIXP_DBL *const staticSpectralCoeffsR =
        pAacDecoderStaticChannelInfo[L]
            ->pCpeStaticData->jointStereoPersistentData.spectralCoeffs[R];
    SHORT *const staticSpecScaleL =
        pAacDecoderStaticChannelInfo[L]
            ->pCpeStaticData->jointStereoPersistentData.specScale[L];
    SHORT *const staticSpecScaleR =
        pAacDecoderStaticChannelInfo[L]
            ->pCpeStaticData->jointStereoPersistentData.specScale[R];

    FIXP_DBL *dmx_re =
        pAacDecoderStaticChannelInfo[L]
            ->pCpeStaticData->jointStereoPersistentData.scratchBuffer;
    FIXP_DBL *dmx_re_prev =
        pAacDecoderStaticChannelInfo[L]
            ->pCpeStaticData->jointStereoPersistentData.scratchBuffer +
        1024;

    /* When MS is applied over the main band this value gets computed. Otherwise
     * (for the tiles) it uses the assigned value */
    SHORT dmx_re_prev_e = *store_dmx_re_prev_e;

    const FIXP_FILT *pCoeff;
    const FIXP_FILT *pCoeffPrev;
    int coeffPointerOffset;

    int previousShape = (int)pJointStereoPersistentData->winShapePrev;
    int currentShape = (int)pAacDecoderChannelInfo[L]->icsInfo.WindowShape;

    /* complex stereo prediction */

    /* 0. preparations */

    /* 0.0. get scratch buffer for downmix MDST */
    C_AALLOC_SCRATCH_START(dmx_im, FIXP_DBL, 1024);

    /* 0.1. window lengths */

    /* get length of short window for current configuration */
    windowLen =
        pAacDecoderChannelInfo[L]->granuleLength; /* framelength 768 => 96,
                                                     framelength 1024 => 128 */

    /* if this is no short-block set length for long-block */
    if (pAacDecoderChannelInfo[L]->icsInfo.WindowSequence != BLOCK_SHORT) {
      windowLen *= 8;
    }

    /* 0.2. set pointer to filter-coefficients for MDST excitation including
     * previous frame portions */
    /*      cp. ISO/IEC FDIS 23003-3:2011(E) table 125 */

    /* set pointer to default-position */
    pCoeffPrev = mdst_filt_coef_prev[previousShape];

    if (cplxPredictionData->complex_coef == 1) {
      switch (pAacDecoderChannelInfo[L]
                  ->icsInfo.WindowSequence) { /* current window sequence */
        case BLOCK_SHORT:
        case BLOCK_LONG:
          pCoeffPrev = mdst_filt_coef_prev[previousShape];
          break;

        case BLOCK_START:
          if ((pJointStereoPersistentData->winSeqPrev == BLOCK_SHORT) ||
              (pJointStereoPersistentData->winSeqPrev == BLOCK_START)) {
            /* a stop-start-sequence can only follow on an eight-short-sequence
             * or a start-sequence */
            pCoeffPrev = mdst_filt_coef_prev[2 + previousShape];
          } else {
            pCoeffPrev = mdst_filt_coef_prev[previousShape];
          }
          break;

        case BLOCK_STOP:
          pCoeffPrev = mdst_filt_coef_prev[2 + previousShape];
          break;

        default:
          pCoeffPrev = mdst_filt_coef_prev[previousShape];
          break;
      }
    }

    /* 0.3. set pointer to filter-coefficients for MDST excitation */

    /* define offset of pointer to filter-coefficients for MDST exitation
     * employing only the current frame */
    if ((previousShape == SHAPE_SINE) && (currentShape == SHAPE_SINE)) {
      coeffPointerOffset = 0;
    } else if ((previousShape == SHAPE_SINE) && (currentShape == SHAPE_KBD)) {
      coeffPointerOffset = 2;
    } else if ((previousShape == SHAPE_KBD) && (currentShape == SHAPE_KBD)) {
      coeffPointerOffset = 1;
    } else /* if ( (previousShape == SHAPE_KBD) && (currentShape == SHAPE_SINE)
              ) */
    {
      coeffPointerOffset = 3;
    }

    /* set pointer to filter-coefficient table cp. ISO/IEC FDIS 23003-3:2011(E)
     * table 124 */
    switch (pAacDecoderChannelInfo[L]
                ->icsInfo.WindowSequence) { /* current window sequence */
      case BLOCK_SHORT:
      case BLOCK_LONG:
        pCoeff = mdst_filt_coef_curr[coeffPointerOffset];
        break;

      case BLOCK_START:
        if ((pJointStereoPersistentData->winSeqPrev == BLOCK_SHORT) ||
            (pJointStereoPersistentData->winSeqPrev == BLOCK_START)) {
          /* a stop-start-sequence can only follow on an eight-short-sequence or
           * a start-sequence */
          pCoeff = mdst_filt_coef_curr[12 + coeffPointerOffset];
        } else {
          pCoeff = mdst_filt_coef_curr[4 + coeffPointerOffset];
        }
        break;

      case BLOCK_STOP:
        pCoeff = mdst_filt_coef_curr[8 + coeffPointerOffset];
        break;

      default:
        pCoeff = mdst_filt_coef_curr[coeffPointerOffset];
    }

    /* 0.4. find maximum common (l/r) band-scaling-factor for whole sequence
     * (all windows) */
    frameMaxScale = 0;
    for (window = 0, group = 0; group < windowGroups; group++) {
      for (groupwin = 0; groupwin < pWindowGroupLength[group];
           groupwin++, window++) {
        SHORT *leftScale = &SFBleftScale[window * 16];
        SHORT *rightScale = &SFBrightScale[window * 16];
        int windowMaxScale = 0;

        /* find maximum scaling factor of all bands in this window */
        for (band = 0; band < min_sfb_ste; band++) {
          int lScale = leftScale[band];
          int rScale = rightScale[band];
          int commonScale = ((lScale > rScale) ? lScale : rScale);
          windowMaxScale =
              (windowMaxScale < commonScale) ? commonScale : windowMaxScale;
        }
        if (scaleFactorBandsTransmittedL >
            min_sfb_ste) { /* i.e. scaleFactorBandsTransmittedL == max_sfb_ste
                            */
          for (; band < max_sfb_ste; band++) {
            int lScale = leftScale[band];
            windowMaxScale =
                (windowMaxScale < lScale) ? lScale : windowMaxScale;
          }
        } else {
          if (scaleFactorBandsTransmittedR >
              min_sfb_ste) { /* i.e. scaleFactorBandsTransmittedR == max_sfb_ste
                              */
            for (; band < max_sfb_ste; band++) {
              int rScale = rightScale[band];
              windowMaxScale =
                  (windowMaxScale < rScale) ? rScale : windowMaxScale;
            }
          }
        }

        /* find maximum common SF of all windows */
        frameMaxScale =
            (frameMaxScale < windowMaxScale) ? windowMaxScale : frameMaxScale;
      }
    }

    /* add some headroom for overflow protection during filter and add operation
     */
    frameMaxScale += 2;

    /* process on window-basis (i.e. iterate over all groups and corresponding
     * windows) */
    for (window = 0, group = 0; group < windowGroups; group++) {
      groupMask = 1 << group;

      for (groupwin = 0; groupwin < pWindowGroupLength[group];
           groupwin++, window++) {
        /* initialize the MDST with zeros */
        FDKmemclear(&dmx_im[windowLen * window], windowLen * sizeof(FIXP_DBL));

        /* 1. calculate the previous downmix MDCT. We do this once just for the
         * Main band. */
        if (cplxPredictionData->complex_coef == 1) {
          if ((cplxPredictionData->use_prev_frame == 1) && (mainband_flag)) {
            /* if this is a long-block or the first window of a short-block
               calculate the downmix MDCT of the previous frame.
               use_prev_frame is assumed not to change during a frame!
            */

            /* first determine shiftfactors to scale left and right channel */
            if ((pAacDecoderChannelInfo[L]->icsInfo.WindowSequence !=
                 BLOCK_SHORT) ||
                (window == 0)) {
              int index_offset = 0;
              int srLeftChan = 0;
              int srRightChan = 0;
              if (pAacDecoderChannelInfo[L]->icsInfo.WindowSequence ==
                  BLOCK_SHORT) {
                /* use the last window of the previous frame for MDCT
                 * calculation if this is a short-block. */
                index_offset = windowLen * 7;
                if (staticSpecScaleL[7] > staticSpecScaleR[7]) {
                  srRightChan = staticSpecScaleL[7] - staticSpecScaleR[7];
                  dmx_re_prev_e = staticSpecScaleL[7];
                } else {
                  srLeftChan = staticSpecScaleR[7] - staticSpecScaleL[7];
                  dmx_re_prev_e = staticSpecScaleR[7];
                }
              } else {
                if (staticSpecScaleL[0] > staticSpecScaleR[0]) {
                  srRightChan = staticSpecScaleL[0] - staticSpecScaleR[0];
                  dmx_re_prev_e = staticSpecScaleL[0];
                } else {
                  srLeftChan = staticSpecScaleR[0] - staticSpecScaleL[0];
                  dmx_re_prev_e = staticSpecScaleR[0];
                }
              }

              /* now scale channels and determine downmix MDCT of previous frame
               */
              if (pAacDecoderStaticChannelInfo[L]
                      ->pCpeStaticData->jointStereoPersistentData
                      .clearSpectralCoeffs == 1) {
                FDKmemclear(dmx_re_prev, windowLen * sizeof(FIXP_DBL));
                dmx_re_prev_e = 0;
              } else {
                if (cplxPredictionData->pred_dir == 0) {
                  for (int i = 0; i < windowLen; i++) {
                    dmx_re_prev[i] =
                        ((staticSpectralCoeffsL[index_offset + i] >>
                          srLeftChan) +
                         (staticSpectralCoeffsR[index_offset + i] >>
                          srRightChan)) >>
                        1;
                  }
                } else {
                  for (int i = 0; i < windowLen; i++) {
                    dmx_re_prev[i] =
                        ((staticSpectralCoeffsL[index_offset + i] >>
                          srLeftChan) -
                         (staticSpectralCoeffsR[index_offset + i] >>
                          srRightChan)) >>
                        1;
                  }
                }
              }

              /* In case that we use INF we have to preserve the state of the
              "dmx_re_prev" (original or computed). This is necessary because we
              have to apply MS over the separate IGF tiles. */
              FDKmemcpy(store_dmx_re_prev, &dmx_re_prev[0],
                        windowLen * sizeof(FIXP_DBL));

              /* Particular exponent of the computed/original "dmx_re_prev" must
               * be kept for the tile MS calculations if necessary.*/
              *store_dmx_re_prev_e = dmx_re_prev_e;

            } /* if ( (pAacDecoderChannelInfo[L]->icsInfo.WindowSequence !=
                 BLOCK_SHORT) || (window == 0) ) */

          } /* if ( pJointStereoData->use_prev_frame == 1 ) */

        } /* if ( pJointStereoData->complex_coef == 1 ) */

        /* 2. calculate downmix MDCT of current frame */

        /* set pointer to scale-factor-bands of current window */
        SHORT *leftScale = &SFBleftScale[window * 16];
        SHORT *rightScale = &SFBrightScale[window * 16];

        specScaleL[window] = specScaleR[window] = frameMaxScale;

        /* adapt scaling-factors to previous frame */
        if (cplxPredictionData->use_prev_frame == 1) {
          if (window == 0) {
            if (dmx_re_prev_e < frameMaxScale) {
              if (mainband_flag == 0) {
                scaleValues(dmx_re_prev, store_dmx_re_prev, windowLen,
                            -(frameMaxScale - dmx_re_prev_e));
              } else {
                for (int i = 0; i < windowLen; i++) {
                  dmx_re_prev[i] >>= (frameMaxScale - dmx_re_prev_e);
                }
              }
            } else {
              if (mainband_flag == 0) {
                FDKmemcpy(dmx_re_prev, store_dmx_re_prev,
                          windowLen * sizeof(FIXP_DBL));
              }
              specScaleL[0] = dmx_re_prev_e;
              specScaleR[0] = dmx_re_prev_e;
            }
          } else { /* window != 0 */
            FDK_ASSERT(pAacDecoderChannelInfo[L]->icsInfo.WindowSequence ==
                       BLOCK_SHORT);
            if (specScaleL[window - 1] < frameMaxScale) {
              for (int i = 0; i < windowLen; i++) {
                dmx_re[windowLen * (window - 1) + i] >>=
                    (frameMaxScale - specScaleL[window - 1]);
              }
            } else {
              specScaleL[window] = specScaleL[window - 1];
              specScaleR[window] = specScaleR[window - 1];
            }
          }
        } /* if ( pJointStereoData->use_prev_frame == 1 ) */

        /* scaling factors of both channels ought to be equal now */
        FDK_ASSERT(specScaleL[window] == specScaleR[window]);

        /* rescale signal and calculate downmix MDCT */
        for (band = 0; band < max_sfb_ste; band++) {
          /* first adapt scaling of current band to scaling of current window =>
           * shift signal right */
          int lScale = leftScale[band];
          int rScale = rightScale[band];

          lScale = fMin(DFRACT_BITS - 1, specScaleL[window] - lScale);
          rScale = fMin(DFRACT_BITS - 1,
                        specScaleL[window] - rScale); /* L or R doesn't
                                                         matter,
                                                         specScales are
                                                         equal at this
                                                         point */

          /* Write back to sfb scale to cover the case when max_sfb_ste <
           * max_sfb */
          leftScale[band] = rightScale[band] = specScaleL[window];

          for (int i = pScaleFactorBandOffsets[band];
               i < pScaleFactorBandOffsets[band + 1]; i++) {
            spectrumL[windowLen * window + i] >>= lScale;
            spectrumR[windowLen * window + i] >>= rScale;
          }

          /* now calculate downmix MDCT */
          if (pJointStereoData->MsUsed[band] & groupMask) {
            for (int i = pScaleFactorBandOffsets[band];
                 i < pScaleFactorBandOffsets[band + 1]; i++) {
              dmx_re[windowLen * window + i] =
                  spectrumL[windowLen * window + i];
            }
          } else {
            if (cplxPredictionData->pred_dir == 0) {
              for (int i = pScaleFactorBandOffsets[band];
                   i < pScaleFactorBandOffsets[band + 1]; i++) {
                dmx_re[windowLen * window + i] =
                    (spectrumL[windowLen * window + i] +
                     spectrumR[windowLen * window + i]) >>
                    1;
              }
            } else {
              for (int i = pScaleFactorBandOffsets[band];
                   i < pScaleFactorBandOffsets[band + 1]; i++) {
                dmx_re[windowLen * window + i] =
                    (spectrumL[windowLen * window + i] -
                     spectrumR[windowLen * window + i]) >>
                    1;
              }
            }
          }

        } /* for ( band=0; band<max_sfb_ste; band++ ) */
        /* Clean until the end */
        for (int i = pScaleFactorBandOffsets[max_sfb_ste_outside];
             i < windowLen; i++) {
          dmx_re[windowLen * window + i] = (FIXP_DBL)0;
        }

        /* 3. calculate MDST-portion corresponding to the current frame. */
        if (cplxPredictionData->complex_coef == 1) {
          {
            /* 3.1 move pointer in filter-coefficient table in case of short
             * window sequence */
            /*     (other coefficients are utilized for the last 7 short
             * windows)            */
            if ((pAacDecoderChannelInfo[L]->icsInfo.WindowSequence ==
                 BLOCK_SHORT) &&
                (window != 0)) {
              pCoeff = mdst_filt_coef_curr[currentShape];
              pCoeffPrev = mdst_filt_coef_prev[currentShape];
            }

            /* The length of the filter processing must be extended because of
             * filter boundary problems */
            int extended_band = fMin(
                pScaleFactorBandOffsets[max_sfb_ste_outside] + 7, windowLen);

            /* 3.2. estimate downmix MDST from current frame downmix MDCT */
            if ((pAacDecoderChannelInfo[L]->icsInfo.WindowSequence ==
                 BLOCK_SHORT) &&
                (window != 0)) {
              CJointStereo_filterAndAdd(&dmx_re[windowLen * window],
                                        extended_band, windowLen, pCoeff,
                                        &dmx_im[windowLen * window], 1);

              CJointStereo_filterAndAdd(&dmx_re[windowLen * (window - 1)],
                                        extended_band, windowLen, pCoeffPrev,
                                        &dmx_im[windowLen * window], 0);
            } else {
              CJointStereo_filterAndAdd(dmx_re, extended_band, windowLen,
                                        pCoeff, dmx_im, 1);

              if (cplxPredictionData->use_prev_frame == 1) {
                CJointStereo_filterAndAdd(dmx_re_prev, extended_band, windowLen,
                                          pCoeffPrev,
                                          &dmx_im[windowLen * window], 0);
              }
            }

          } /* if(pAacDecoderChannelInfo[L]->transform_splitting_active) */
        }   /* if ( pJointStereoData->complex_coef == 1 ) */

        /* 4. upmix process */
        INT pred_dir = cplxPredictionData->pred_dir ? -1 : 1;
        /* 0.1 in Q-3.34 */
        const FIXP_DBL pointOne = 0x66666666; /* 0.8 */
        /* Shift value for the downmix */
        const INT shift_dmx = SF_FNA_COEFFS + 1;

        for (band = 0; band < max_sfb_ste_outside; band++) {
          if (pJointStereoData->MsUsed[band] & groupMask) {
            FIXP_SGL tempRe =
                (FIXP_SGL)cplxPredictionData->alpha_q_re[group][band];
            FIXP_SGL tempIm =
                (FIXP_SGL)cplxPredictionData->alpha_q_im[group][band];

            /* Find the minimum common headroom for alpha_re and alpha_im */
            int alpha_re_headroom = CountLeadingBits((INT)tempRe) - 16;
            if (tempRe == (FIXP_SGL)0) alpha_re_headroom = 15;
            int alpha_im_headroom = CountLeadingBits((INT)tempIm) - 16;
            if (tempIm == (FIXP_SGL)0) alpha_im_headroom = 15;
            int val = fMin(alpha_re_headroom, alpha_im_headroom);

            /* Multiply alpha by 0.1 with maximum precision */
            FDK_ASSERT(val >= 0);
            FIXP_DBL alpha_re_tmp = fMult((FIXP_SGL)(tempRe << val), pointOne);
            FIXP_DBL alpha_im_tmp = fMult((FIXP_SGL)(tempIm << val), pointOne);

            /* Calculate alpha exponent */
            /* (Q-3.34 * Q15.0) shifted left by "val" */
            int alpha_re_exp = -3 + 15 - val;

            int help3_shift = alpha_re_exp + 1;

            FIXP_DBL *p2CoeffL = &(
                spectrumL[windowLen * window + pScaleFactorBandOffsets[band]]);
            FIXP_DBL *p2CoeffR = &(
                spectrumR[windowLen * window + pScaleFactorBandOffsets[band]]);
            FIXP_DBL *p2dmxIm =
                &(dmx_im[windowLen * window + pScaleFactorBandOffsets[band]]);
            FIXP_DBL *p2dmxRe =
                &(dmx_re[windowLen * window + pScaleFactorBandOffsets[band]]);

            for (int i = pScaleFactorBandOffsets[band];
                 i < pScaleFactorBandOffsets[band + 1]; i++) {
              /* Calculating helper term:
                    side = specR[i] - alpha_re[i] * dmx_re[i] - alpha_im[i] *
                dmx_im[i];

                Here "dmx_re" may be the same as "specL" or alternatively keep
                the downmix. "dmx_re" and "specL" are two different pointers
                pointing to separate arrays, which may or may not contain the
                same data (with different scaling).
              */

              /* help1: alpha_re[i] * dmx_re[i] */
              FIXP_DBL help1 = fMultDiv2(alpha_re_tmp, *p2dmxRe++);

              /* tmp: dmx_im[i] */
              FIXP_DBL tmp = (*p2dmxIm++) << shift_dmx;

              /* help2: alpha_im[i] * dmx_im[i] */
              FIXP_DBL help2 = fMultDiv2(alpha_im_tmp, tmp);

              /* help3: alpha_re[i] * dmx_re[i] + alpha_im[i] * dmx_im[i] */
              FIXP_DBL help3 = help1 + help2;

              /* side (= help4) = specR[i] - (dmx_re[i] * specL[i] + alpha_im[i]
               * * dmx_im[i]) */
              FIXP_DBL help4 = *p2CoeffR - scaleValue(help3, help3_shift);

              /* We calculate the left and right output by using the helper
               * function */
              /* specR[i] = -/+ (specL[i] - side); */
              *p2CoeffR =
                  (FIXP_DBL)((LONG)(*p2CoeffL - help4) * (LONG)pred_dir);
              p2CoeffR++;

              /* specL[i] = specL[i] + side; */
              *p2CoeffL = *p2CoeffL + help4;
              p2CoeffL++;
            }
          }

        } /* for ( band=0; band < max_sfb_ste; band++ ) */
      }   /* for ( groupwin=0; groupwin<pWindowGroupLength[group]; groupwin++,
             window++ ) */

    } /* for ( window = 0, group = 0; group < windowGroups; group++ ) */

    /* free scratch buffer */
    C_AALLOC_SCRATCH_END(dmx_im, FIXP_DBL, 1024);

  } else {
    /* MS stereo */

    for (window = 0, group = 0; group < windowGroups; group++) {
      groupMask = 1 << group;

      for (int groupwin = 0; groupwin < pWindowGroupLength[group];
           groupwin++, window++) {
        FIXP_DBL *leftSpectrum, *rightSpectrum;
        SHORT *leftScale = &SFBleftScale[window * 16];
        SHORT *rightScale = &SFBrightScale[window * 16];

        leftSpectrum =
            SPEC(spectrumL, window, pAacDecoderChannelInfo[L]->granuleLength);
        rightSpectrum =
            SPEC(spectrumR, window, pAacDecoderChannelInfo[R]->granuleLength);

        for (band = 0; band < max_sfb_ste_outside; band++) {
          if (pJointStereoData->MsUsed[band] & groupMask) {
            int lScale = leftScale[band];
            int rScale = rightScale[band];
            int commonScale = lScale > rScale ? lScale : rScale;
            unsigned int offsetCurrBand, offsetNextBand;

            /* ISO/IEC 14496-3 Chapter 4.6.8.1.1 :
               M/S joint channel coding can only be used if common_window is 1.
             */
            FDK_ASSERT(GetWindowSequence(&pAacDecoderChannelInfo[L]->icsInfo) ==
                       GetWindowSequence(&pAacDecoderChannelInfo[R]->icsInfo));
            FDK_ASSERT(GetWindowShape(&pAacDecoderChannelInfo[L]->icsInfo) ==
                       GetWindowShape(&pAacDecoderChannelInfo[R]->icsInfo));

            commonScale++;
            leftScale[band] = commonScale;
            rightScale[band] = commonScale;

            lScale = fMin(DFRACT_BITS - 1, commonScale - lScale);
            rScale = fMin(DFRACT_BITS - 1, commonScale - rScale);

            FDK_ASSERT(lScale >= 0 && rScale >= 0);

            offsetCurrBand = pScaleFactorBandOffsets[band];
            offsetNextBand = pScaleFactorBandOffsets[band + 1];

            CJointStereo_GenerateMSOutput(&(leftSpectrum[offsetCurrBand]),
                                          &(rightSpectrum[offsetCurrBand]),
                                          lScale, rScale,
                                          offsetNextBand - offsetCurrBand);
          }
        }
        if (scaleFactorBandsTransmittedL > scaleFactorBandsTransmitted) {
          for (; band < scaleFactorBandsTransmittedL; band++) {
            if (pJointStereoData->MsUsed[band] & groupMask) {
              rightScale[band] = leftScale[band];

              for (int index = pScaleFactorBandOffsets[band];
                   index < pScaleFactorBandOffsets[band + 1]; index++) {
                FIXP_DBL leftCoefficient = leftSpectrum[index];
                /* FIXP_DBL rightCoefficient = (FIXP_DBL)0; */
                rightSpectrum[index] = leftCoefficient;
              }
            }
          }
        } else if (scaleFactorBandsTransmittedR > scaleFactorBandsTransmitted) {
          for (; band < scaleFactorBandsTransmittedR; band++) {
            if (pJointStereoData->MsUsed[band] & groupMask) {
              leftScale[band] = rightScale[band];

              for (int index = pScaleFactorBandOffsets[band];
                   index < pScaleFactorBandOffsets[band + 1]; index++) {
                /* FIXP_DBL leftCoefficient  = (FIXP_DBL)0; */
                FIXP_DBL rightCoefficient = rightSpectrum[index];

                leftSpectrum[index] = rightCoefficient;
                rightSpectrum[index] = -rightCoefficient;
              }
            }
          }
        }
      }
    }

    /* Reset MsUsed flags if no explicit signalling was transmitted. Necessary
       for intensity coding. PNS correlation signalling was mapped before
       calling CJointStereo_ApplyMS(). */
    if (pJointStereoData->MsMaskPresent == 2) {
      FDKmemclear(pJointStereoData->MsUsed,
                  JointStereoMaximumBands * sizeof(UCHAR));
    }
  }
}

void CJointStereo_ApplyIS(CAacDecoderChannelInfo *pAacDecoderChannelInfo[2],
                          const SHORT *pScaleFactorBandOffsets,
                          const UCHAR *pWindowGroupLength,
                          const int windowGroups,
                          const int scaleFactorBandsTransmitted) {
  CJointStereoData *pJointStereoData =
      &pAacDecoderChannelInfo[L]->pComData->jointStereoData;

  for (int window = 0, group = 0; group < windowGroups; group++) {
    UCHAR *CodeBook;
    SHORT *ScaleFactor;
    UCHAR groupMask = 1 << group;

    CodeBook = &pAacDecoderChannelInfo[R]->pDynData->aCodeBook[group * 16];
    ScaleFactor =
        &pAacDecoderChannelInfo[R]->pDynData->aScaleFactor[group * 16];

    for (int groupwin = 0; groupwin < pWindowGroupLength[group];
         groupwin++, window++) {
      FIXP_DBL *leftSpectrum, *rightSpectrum;
      SHORT *leftScale =
          &pAacDecoderChannelInfo[L]->pDynData->aSfbScale[window * 16];
      SHORT *rightScale =
          &pAacDecoderChannelInfo[R]->pDynData->aSfbScale[window * 16];
      int band;

      leftSpectrum = SPEC(pAacDecoderChannelInfo[L]->pSpectralCoefficient,
                          window, pAacDecoderChannelInfo[L]->granuleLength);
      rightSpectrum = SPEC(pAacDecoderChannelInfo[R]->pSpectralCoefficient,
                           window, pAacDecoderChannelInfo[R]->granuleLength);

      for (band = 0; band < scaleFactorBandsTransmitted; band++) {
        if ((CodeBook[band] == INTENSITY_HCB) ||
            (CodeBook[band] == INTENSITY_HCB2)) {
          int bandScale = -(ScaleFactor[band] + 100);

          int msb = bandScale >> 2;
          int lsb = bandScale & 0x03;

          /* exponent of MantissaTable[lsb][0] is 1, thus msb+1 below. */
          FIXP_DBL scale = MantissaTable[lsb][0];

          /* ISO/IEC 14496-3 Chapter 4.6.8.2.3 :
             The use of intensity stereo coding is signaled by the use of the
             pseudo codebooks INTENSITY_HCB and INTENSITY_HCB2 (15 and 14) only
             in the right channel of a channel_pair_element() having a common
             ics_info() (common_window == 1). */
          FDK_ASSERT(GetWindowSequence(&pAacDecoderChannelInfo[L]->icsInfo) ==
                     GetWindowSequence(&pAacDecoderChannelInfo[R]->icsInfo));
          FDK_ASSERT(GetWindowShape(&pAacDecoderChannelInfo[L]->icsInfo) ==
                     GetWindowShape(&pAacDecoderChannelInfo[R]->icsInfo));

          rightScale[band] = leftScale[band] + msb + 1;

          if (pJointStereoData->MsUsed[band] & groupMask) {
            if (CodeBook[band] == INTENSITY_HCB) /* _NOT_ in-phase */
            {
              scale = -scale;
            }
          } else {
            if (CodeBook[band] == INTENSITY_HCB2) /* out-of-phase */
            {
              scale = -scale;
            }
          }

          for (int index = pScaleFactorBandOffsets[band];
               index < pScaleFactorBandOffsets[band + 1]; index++) {
            rightSpectrum[index] = fMult(leftSpectrum[index], scale);
          }
        }
      }
    }
  }
}
