/******************************************************************************
 *                                                                            *
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 *****************************************************************************
 * Originally developed and contributed by Ittiam Systems Pvt. Ltd, Bangalore
*/
#include <string.h>

#include "ixheaacd_sbr_common.h"
#include <ixheaacd_type_def.h>

#include "ixheaacd_constants.h"
#include "ixheaacd_basic_ops32.h"
#include "ixheaacd_basic_ops16.h"
#include "ixheaacd_basic_ops40.h"
#include "ixheaacd_basic_ops.h"

#include "ixheaacd_intrinsics.h"
#include "ixheaacd_common_rom.h"
#include "ixheaacd_bitbuffer.h"
#include "ixheaacd_sbrdecsettings.h"
#include "ixheaacd_sbr_scale.h"
#include "ixheaacd_lpp_tran.h"
#include "ixheaacd_env_extr_part.h"
#include "ixheaacd_sbr_rom.h"
#include "ixheaacd_hybrid.h"
#include "ixheaacd_ps_dec.h"
#include "ixheaacd_env_extr.h"
#include "ixheaacd_qmf_dec.h"

#include <ixheaacd_basic_op.h>
#include "ixheaacd_env_calc.h"

#include "ixheaacd_interface.h"

#include "ixheaacd_function_selector.h"
#include "ixheaacd_audioobjtypes.h"

#define DCT3_LEN (32)
#define DCT2_LEN (64)

#define LP_SHIFT_VAL 7
#define HQ_SHIFT_64 4
#define RADIXSHIFT 1
#define ROUNDING_SPECTRA 1
#define HQ_SHIFT_VAL 4

static PLATFORM_INLINE WORD32 ixheaacd_mult32x32in32_shift25(WORD32 a,
                                                             WORD32 b) {
  WORD32 result;
  WORD64 temp_result;

  temp_result = (WORD64)a * (WORD64)b;

  result = (WORD32)(temp_result >> 25);

  return (result);
}

VOID ixheaacd_pretwdct2(WORD32 *inp, WORD32 *out_fwd) {
  WORD32 n;
  WORD32 *out_rev = out_fwd + DCT2_LEN - 1;

  for (n = 0; n < DCT2_LEN / 2; n++) {
    *out_fwd = *inp;
    inp++;
    *out_rev = *inp;
    out_fwd++;

    out_rev--;
    inp++;
  }

  return;
}

static PLATFORM_INLINE VOID ixheaacd_pretwdct2_32(WORD32 *inp, WORD32 *out_fwd,
                                                  int dct2_len) {
  WORD32 n;

  WORD32 *out_rev = out_fwd + dct2_len - 1;
  for (n = dct2_len / 2 - 1; n >= 0; n--) {
    *out_fwd = *inp;
    inp++;
    *out_rev = *inp;
    out_fwd++;

    out_rev--;
    inp++;
  }

  return;
}

VOID ixheaacd_fftposttw(WORD32 *out,
                        ia_qmf_dec_tables_struct *qmf_dec_tables_ptr) {
  int k;
  WORD32 *p_out_fwd, *ptr_out_rev;
  const WORD16 *twidle_fwd, *twidle_rev;
  WORD32 in1, in2, val1, val2;

  twidle_fwd = qmf_dec_tables_ptr->post_fft_tbl + 1;
  twidle_rev = qmf_dec_tables_ptr->post_fft_tbl + 15;

  p_out_fwd = out;
  ptr_out_rev = out + DCT2_LEN - 1;

  in1 = ((*p_out_fwd++) << 1);
  val1 = ((*p_out_fwd--) << 1);

  *p_out_fwd++ = in1;
  *p_out_fwd++ = val1;

  for (k = 1; k <= DCT2_LEN / 4; k++) {
    WORD32 temp[4];
    WORD16 twid_re, twid_im;

    temp[0] = *p_out_fwd++;
    temp[1] = *p_out_fwd--;
    temp[3] = *ptr_out_rev--;
    temp[2] = *ptr_out_rev++;

    in2 = temp[3] - temp[1];
    in1 = temp[3] + temp[1];

    temp[1] = temp[0] - temp[2];
    temp[3] = temp[0] + temp[2];

    twid_re = *twidle_fwd++;
    twid_im = *twidle_rev--;
    val1 = ixheaacd_mult32x16in32(in1, twid_re) -
           ixheaacd_mult32x16in32(temp[1], twid_im);
    val2 = ixheaacd_mult32x16in32(temp[1], twid_re) +
           ixheaacd_mult32x16in32(in1, twid_im);
    val1 = val1 << 1;
    val2 = val2 << 1;

    *p_out_fwd++ = temp[3] + val1;
    *p_out_fwd++ = in2 + val2;

    *ptr_out_rev-- = -in2 + val2;
    *ptr_out_rev-- = temp[3] - val1;
  }

  return;
}

VOID ixheaacd_posttwdct2(WORD32 *inp, WORD16 *out_fwd,
                         ia_qmf_dec_tables_struct *qmf_dec_tables_ptr) {
  WORD32 k;
  WORD32 inp_re, inp_im, out_re, out_im, last_val, out_re1;
  WORD16 *out_fwd2, *out_rev2, *out_rev;
  WORD16 twid_re, twid_im;
  const WORD16 *twidle_fwd;
  WORD16 re1, im1, im2;

  out_rev = out_fwd + DCT2_LEN - 1;
  out_rev2 = out_fwd - 1;
  out_fwd2 = out_fwd + 65;
  out_re = *inp++;
  out_im = *inp++;
  out_re1 = (out_re + out_im) >> 1;
  re1 = ixheaacd_round16(ixheaacd_shl32(out_re1, (5 - 1)));

  *out_fwd++ = re1;

  last_val = (out_re - out_im);

  twidle_fwd = qmf_dec_tables_ptr->dct23_tw + 2;
  for (k = DCT2_LEN / 2 - 2; k >= 0; k--) {
    inp_re = *inp++;
    inp_im = *inp++;

    twid_re = *twidle_fwd++;
    twid_im = *twidle_fwd++;
    out_re = ixheaacd_sub32(ixheaacd_mult32x16in32(inp_re, twid_re),
                            ixheaacd_mult32x16in32(inp_im, twid_im));
    out_im = ixheaacd_add32(ixheaacd_mult32x16in32(inp_im, twid_re),
                            ixheaacd_mult32x16in32(inp_re, twid_im));
    re1 = ixheaacd_round16(ixheaacd_shl32(out_re, (5 - 1)));
    im1 = ixheaacd_round16(ixheaacd_shl32(out_im, (5 - 1)));
    im2 = ixheaacd_negate16(im1);

    *out_fwd++ = re1;
    *out_rev2-- = re1;
    *out_rev-- = im1;
    *out_fwd2++ = im2;
  }
  twid_re = *twidle_fwd++;

  out_re = ixheaacd_mult32x16in32(last_val, twid_re);
  re1 = ixheaacd_round16(ixheaacd_shl32(out_re, (5 - 1)));

  *out_fwd++ = re1;
  *out_rev2-- = re1;

  return;
}

static PLATFORM_INLINE VOID ixheaacd_fftposttw_32(
    WORD32 *out, int dct2_len, ia_qmf_dec_tables_struct *qmf_dec_tables_ptr) {
  int k;
  WORD32 *ptr_out_fwd, *ptr_out_rev;
  const WORD16 *twidle_fwd, *twidle_rev;
  WORD32 in1, in2, val1, val2;

  twidle_fwd = qmf_dec_tables_ptr->post_fft_tbl + 2;
  twidle_rev = qmf_dec_tables_ptr->post_fft_tbl + 14;

  ptr_out_fwd = out;
  ptr_out_rev = out + dct2_len - 1;

  in1 = ((*ptr_out_fwd++) << 1);
  val1 = ((*ptr_out_fwd--) << 1);

  *ptr_out_fwd++ = in1;
  *ptr_out_fwd++ = val1;

  for (k = dct2_len / 4 - 1; k >= 0; k--) {
    WORD32 temp0, temp1, temp2, temp3;
    WORD16 twid_re, twid_im;

    temp0 = *ptr_out_fwd++;
    temp1 = *ptr_out_fwd--;
    temp3 = *ptr_out_rev--;
    temp2 = *ptr_out_rev++;

    in1 = temp1 + temp3;
    in2 = -temp1 + temp3;

    temp1 = temp0 - temp2;
    temp3 = temp0 + temp2;

    twid_re = *twidle_fwd;
    twidle_fwd += 2;

    twid_im = *twidle_rev;
    twidle_rev -= 2;

    val1 = ixheaacd_mult32x16in32(in1, twid_re) -
           ixheaacd_mult32x16in32(temp1, twid_im);
    val2 = ixheaacd_mult32x16in32(temp1, twid_re) +
           ixheaacd_mult32x16in32(in1, twid_im);

    val1 = val1 << 1;
    val2 = val2 << 1;

    *ptr_out_fwd++ = temp3 + val1;
    *ptr_out_fwd++ = in2 + val2;

    *ptr_out_rev-- = -in2 + val2;
    *ptr_out_rev-- = temp3 - val1;
  }

  return;
}

static PLATFORM_INLINE VOID
ixheaacd_posttwdct2_32(WORD32 *inp, WORD16 *out_fwd,
                       ia_qmf_dec_tables_struct *qmf_dec_tables_ptr) {
  int k;
  WORD32 inp_re, out_re, out_im, last_val, out_re1;
  WORD16 *out_rev, *out_rev2, *out_fwd2;
  WORD16 twid_re, twid_im;
  const WORD16 *twidle_fwd;
  WORD16 re1, im1, im2;
  WORD32 rounding_fac = 0x8000;

  out_rev = out_fwd + 32 - 1;
  out_rev2 = out_fwd - 1;
  out_fwd2 = out_fwd + 32 + 1;
  out_fwd[32] = 0;

  out_re = *inp++;
  out_im = *inp++;

  out_re1 = (out_re + out_im) >> 1;
  re1 = ixheaacd_round16(ixheaacd_shl32_sat(out_re1, (5 - 1)));
  *out_fwd++ = re1;
  last_val = (out_re - out_im);

  twidle_fwd = qmf_dec_tables_ptr->dct23_tw + 4;
  for (k = 14; k >= 0; k--) {
    WORD32 temp1, temp2;
    inp_re = *inp++;
    twid_re = *twidle_fwd++;
    twid_im = *twidle_fwd;
    twidle_fwd += 3;

    temp1 = ixheaacd_mult32x16in32(inp_re, twid_re);
    temp2 = ixheaacd_mult32x16in32(inp_re, twid_im);

    inp_re = *inp++;

    out_re = ixheaacd_sub32(temp1, ixheaacd_mult32x16in32(inp_re, twid_im));
    out_im = ixheaacd_add32(ixheaacd_mult32x16in32(inp_re, twid_re), temp2);

    out_re = ixheaacd_add32_sat(out_re, out_re);
    out_im = ixheaacd_add32_sat(out_im, out_im);
    out_re = ixheaacd_add32_sat(out_re, out_re);
    out_im = ixheaacd_add32_sat(out_im, out_im);
    out_re = ixheaacd_add32_sat(out_re, out_re);
    out_im = ixheaacd_add32_sat(out_im, out_im);
    out_re = ixheaacd_add32_sat(out_re, out_re);
    out_im = ixheaacd_add32_sat(out_im, out_im);
    out_re = ixheaacd_add32_sat(out_re, rounding_fac);
    out_im = ixheaacd_add32_sat(out_im, rounding_fac);
    re1 = (out_re >> 16);
    im1 = (out_im >> 16);
    im2 = ixheaacd_negate16(im1);

    *out_fwd++ = re1;
    *out_rev2-- = re1;
    *out_rev-- = im1;
    *out_fwd2++ = im2;
  }
  twid_re = *twidle_fwd++;

  out_re = ixheaacd_mult32x16in32(last_val, twid_re);
  re1 = ixheaacd_round16(ixheaacd_shl32_sat(out_re, (5 - 1)));
  *out_fwd++ = re1;
  *out_rev2-- = re1;

  return;
}

VOID ixheaacd_dct2_32(WORD32 *inp, WORD32 *out,
                      ia_qmf_dec_tables_struct *qmf_dec_tables_ptr,
                      WORD16 *filter_states) {
  WORD32 *output;

  output = out + 16;
  filter_states = filter_states + 16;
  ixheaacd_pretwdct2_32(inp, output, 32);

  ixheaacd_radix4bfly(qmf_dec_tables_ptr->w_16, output, 1, 4);
  ixheaacd_postradixcompute4(inp, output, qmf_dec_tables_ptr->dig_rev_table4_16,
                             16);
  ixheaacd_fftposttw_32(inp, 32, qmf_dec_tables_ptr);

  ixheaacd_posttwdct2_32(inp, filter_states, qmf_dec_tables_ptr);

  return;
}

VOID ixheaacd_sbr_qmfanal32_winadd_eld(WORD16 *inp1, WORD16 *inp2,
                                       WORD16 *p_qmf1, WORD16 *p_qmf2,
                                       WORD32 *p_out) {
  WORD32 n;

  for (n = 0; n < 32; n += 2) {
    WORD32 accu;
    accu = ixheaacd_mult16x16in32(inp1[n + 0], p_qmf1[(n + 0)]);
    accu = ixheaacd_add32(
        accu, ixheaacd_mult16x16in32(inp1[n + 64], p_qmf1[(n + 64)]));
    accu = ixheaacd_add32(
        accu, ixheaacd_mult16x16in32(inp1[n + 128], p_qmf1[(n + 128)]));
    accu = ixheaacd_add32(
        accu, ixheaacd_mult16x16in32(inp1[n + 192], p_qmf1[(n + 192)]));
    accu = ixheaacd_add32(
        accu, ixheaacd_mult16x16in32(inp1[n + 256], p_qmf1[(n + 256)]));
    p_out[n] = accu;

    accu = ixheaacd_mult16x16in32(inp1[n + 1 + 0], p_qmf1[(n + 1 + 0)]);
    accu = ixheaacd_add32_sat(
        accu, ixheaacd_mult16x16in32(inp1[n + 1 + 64], p_qmf1[(n + 1 + 64)]));
    accu = ixheaacd_add32_sat(
        accu, ixheaacd_mult16x16in32(inp1[n + 1 + 128], p_qmf1[(n + 1 + 128)]));
    accu = ixheaacd_add32_sat(
        accu, ixheaacd_mult16x16in32(inp1[n + 1 + 192], p_qmf1[(n + 1 + 192)]));
    accu = ixheaacd_add32_sat(
        accu, ixheaacd_mult16x16in32(inp1[n + 1 + 256], p_qmf1[(n + 1 + 256)]));
    p_out[n + 1] = accu;

    accu = ixheaacd_mult16x16in32(inp2[n + 0], p_qmf2[(n + 0)]);
    accu = ixheaacd_add32(
        accu, ixheaacd_mult16x16in32(inp2[n + 64], p_qmf2[(n + 64)]));
    accu = ixheaacd_add32(
        accu, ixheaacd_mult16x16in32(inp2[n + 128], p_qmf2[(n + 128)]));
    accu = ixheaacd_add32(
        accu, ixheaacd_mult16x16in32(inp2[n + 192], p_qmf2[(n + 192)]));
    accu = ixheaacd_add32(
        accu, ixheaacd_mult16x16in32(inp2[n + 256], p_qmf2[(n + 256)]));
    p_out[n + 32] = accu;

    accu = ixheaacd_mult16x16in32(inp2[n + 1 + 0], p_qmf2[(n + 1 + 0)]);
    accu = ixheaacd_add32_sat(
        accu, ixheaacd_mult16x16in32(inp2[n + 1 + 64], p_qmf2[(n + 1 + 64)]));
    accu = ixheaacd_add32_sat(
        accu, ixheaacd_mult16x16in32(inp2[n + 1 + 128], p_qmf2[(n + 1 + 128)]));
    accu = ixheaacd_add32_sat(
        accu, ixheaacd_mult16x16in32(inp2[n + 1 + 192], p_qmf2[(n + 1 + 192)]));
    accu = ixheaacd_add32_sat(
        accu, ixheaacd_mult16x16in32(inp2[n + 1 + 256], p_qmf2[(n + 1 + 256)]));
    p_out[n + 1 + 32] = accu;
  }
}

VOID ixheaacd_esbr_qmfanal32_winadd(WORD32 *inp1, WORD32 *inp2, WORD32 *p_qmf1,
                                    WORD32 *p_qmf2, WORD32 *p_out,
                                    WORD32 num_band_anal_qmf) {
  WORD32 n;
  WORD64 accu;

  if (num_band_anal_qmf == 32) {
    for (n = 0; n < num_band_anal_qmf; n += 2) {
      accu = ixheaacd_mult64(inp1[n + 0], p_qmf1[2 * (n + 0)]);
      accu = ixheaacd_add64(
          accu, ixheaacd_mult64(inp1[n + 2 * num_band_anal_qmf],
                                p_qmf1[2 * (n + 2 * num_band_anal_qmf)]));
      accu = ixheaacd_add64(
          accu, ixheaacd_mult64(inp1[n + 4 * num_band_anal_qmf],
                                p_qmf1[2 * (n + 4 * num_band_anal_qmf)]));
      accu = ixheaacd_add64(
          accu, ixheaacd_mult64(inp1[n + 6 * num_band_anal_qmf],
                                p_qmf1[2 * (n + 6 * num_band_anal_qmf)]));
      accu = ixheaacd_add64(
          accu, ixheaacd_mult64(inp1[n + 8 * num_band_anal_qmf],
                                p_qmf1[2 * (n + 8 * num_band_anal_qmf)]));
      p_out[n] = (WORD32)(accu >> 31);

      accu = ixheaacd_mult64(inp1[n + 1 + 0], p_qmf1[2 * (n + 1 + 0)]);
      accu = ixheaacd_add64(
          accu, ixheaacd_mult64(inp1[n + 1 + 2 * num_band_anal_qmf],
                                p_qmf1[2 * (n + 1 + 2 * num_band_anal_qmf)]));
      accu = ixheaacd_add64(
          accu, ixheaacd_mult64(inp1[n + 1 + 4 * num_band_anal_qmf],
                                p_qmf1[2 * (n + 1 + 4 * num_band_anal_qmf)]));
      accu = ixheaacd_add64(
          accu, ixheaacd_mult64(inp1[n + 1 + 6 * num_band_anal_qmf],
                                p_qmf1[2 * (n + 1 + 6 * num_band_anal_qmf)]));
      accu = ixheaacd_add64(
          accu, ixheaacd_mult64(inp1[n + 1 + 8 * num_band_anal_qmf],
                                p_qmf1[2 * (n + 1 + 8 * num_band_anal_qmf)]));
      p_out[n + 1] = (WORD32)(accu >> 31);

      accu = ixheaacd_mult64(inp2[n + 0], p_qmf2[2 * (n + 0)]);
      accu = ixheaacd_add64(
          accu, ixheaacd_mult64(inp2[n + 2 * num_band_anal_qmf],
                                p_qmf2[2 * (n + 2 * num_band_anal_qmf)]));
      accu = ixheaacd_add64(
          accu, ixheaacd_mult64(inp2[n + 4 * num_band_anal_qmf],
                                p_qmf2[2 * (n + 4 * num_band_anal_qmf)]));
      accu = ixheaacd_add64(
          accu, ixheaacd_mult64(inp2[n + 6 * num_band_anal_qmf],
                                p_qmf2[2 * (n + 6 * num_band_anal_qmf)]));
      accu = ixheaacd_add64(
          accu, ixheaacd_mult64(inp2[n + 8 * num_band_anal_qmf],
                                p_qmf2[2 * (n + 8 * num_band_anal_qmf)]));
      p_out[n + num_band_anal_qmf] = (WORD32)(accu >> 31);

      accu = ixheaacd_mult64(inp2[n + 1 + 0], p_qmf2[2 * (n + 1 + 0)]);
      accu = ixheaacd_add64(
          accu, ixheaacd_mult64(inp2[n + 1 + 2 * num_band_anal_qmf],
                                p_qmf2[2 * (n + 1 + 2 * num_band_anal_qmf)]));
      accu = ixheaacd_add64(
          accu, ixheaacd_mult64(inp2[n + 1 + 4 * num_band_anal_qmf],
                                p_qmf2[2 * (n + 1 + 4 * num_band_anal_qmf)]));
      accu = ixheaacd_add64(
          accu, ixheaacd_mult64(inp2[n + 1 + 6 * num_band_anal_qmf],
                                p_qmf2[2 * (n + 1 + 6 * num_band_anal_qmf)]));
      accu = ixheaacd_add64(
          accu, ixheaacd_mult64(inp2[n + 1 + 8 * num_band_anal_qmf],
                                p_qmf2[2 * (n + 1 + 8 * num_band_anal_qmf)]));
      p_out[n + 1 + num_band_anal_qmf] = (WORD32)(accu >> 31);
    }
  } else if (num_band_anal_qmf == 24) {
    for (n = 0; n < num_band_anal_qmf; n += 2) {
      accu = ixheaacd_mult64(inp1[n + 0], p_qmf1[(n + 0)]);
      accu = ixheaacd_add64(
          accu, ixheaacd_mult64(inp1[n + 2 * num_band_anal_qmf],
                                p_qmf1[(n + 2 * num_band_anal_qmf)]));
      accu = ixheaacd_add64(
          accu, ixheaacd_mult64(inp1[n + 4 * num_band_anal_qmf],
                                p_qmf1[(n + 4 * num_band_anal_qmf)]));
      accu = ixheaacd_add64(
          accu, ixheaacd_mult64(inp1[n + 6 * num_band_anal_qmf],
                                p_qmf1[(n + 6 * num_band_anal_qmf)]));
      accu = ixheaacd_add64(
          accu, ixheaacd_mult64(inp1[n + 8 * num_band_anal_qmf],
                                p_qmf1[(n + 8 * num_band_anal_qmf)]));
      p_out[n] = (WORD32)(accu >> 31);

      accu = ixheaacd_mult64(inp1[n + 1 + 0], p_qmf1[(n + 1 + 0)]);
      accu = ixheaacd_add64(
          accu, ixheaacd_mult64(inp1[n + 1 + 2 * num_band_anal_qmf],
                                p_qmf1[(n + 1 + 2 * num_band_anal_qmf)]));
      accu = ixheaacd_add64(
          accu, ixheaacd_mult64(inp1[n + 1 + 4 * num_band_anal_qmf],
                                p_qmf1[(n + 1 + 4 * num_band_anal_qmf)]));
      accu = ixheaacd_add64(
          accu, ixheaacd_mult64(inp1[n + 1 + 6 * num_band_anal_qmf],
                                p_qmf1[(n + 1 + 6 * num_band_anal_qmf)]));
      accu = ixheaacd_add64(
          accu, ixheaacd_mult64(inp1[n + 1 + 8 * num_band_anal_qmf],
                                p_qmf1[(n + 1 + 8 * num_band_anal_qmf)]));
      p_out[n + 1] = (WORD32)(accu >> 31);

      accu = ixheaacd_mult64(inp2[n + 0], p_qmf2[(n + 0)]);
      accu = ixheaacd_add64(
          accu, ixheaacd_mult64(inp2[n + 2 * num_band_anal_qmf],
                                p_qmf2[(n + 2 * num_band_anal_qmf)]));
      accu = ixheaacd_add64(
          accu, ixheaacd_mult64(inp2[n + 4 * num_band_anal_qmf],
                                p_qmf2[(n + 4 * num_band_anal_qmf)]));
      accu = ixheaacd_add64(
          accu, ixheaacd_mult64(inp2[n + 6 * num_band_anal_qmf],
                                p_qmf2[(n + 6 * num_band_anal_qmf)]));
      accu = ixheaacd_add64(
          accu, ixheaacd_mult64(inp2[n + 8 * num_band_anal_qmf],
                                p_qmf2[(n + 8 * num_band_anal_qmf)]));
      p_out[n + num_band_anal_qmf] = (WORD32)(accu >> 31);

      accu = ixheaacd_mult64(inp2[n + 1 + 0], p_qmf2[(n + 1 + 0)]);
      accu = ixheaacd_add64(
          accu, ixheaacd_mult64(inp2[n + 1 + 2 * num_band_anal_qmf],
                                p_qmf2[(n + 1 + 2 * num_band_anal_qmf)]));
      accu = ixheaacd_add64(
          accu, ixheaacd_mult64(inp2[n + 1 + 4 * num_band_anal_qmf],
                                p_qmf2[(n + 1 + 4 * num_band_anal_qmf)]));
      accu = ixheaacd_add64(
          accu, ixheaacd_mult64(inp2[n + 1 + 6 * num_band_anal_qmf],
                                p_qmf2[(n + 1 + 6 * num_band_anal_qmf)]));
      accu = ixheaacd_add64(
          accu, ixheaacd_mult64(inp2[n + 1 + 8 * num_band_anal_qmf],
                                p_qmf2[(n + 1 + 8 * num_band_anal_qmf)]));
      p_out[n + 1 + num_band_anal_qmf] = (WORD32)(accu >> 31);
    }

  } else {
    for (n = 0; n < num_band_anal_qmf; n += 2) {
      accu = ixheaacd_mult64(inp1[n + 0], p_qmf1[4 * (n + 0)]);
      accu = ixheaacd_add64(
          accu, ixheaacd_mult64(inp1[n + 2 * num_band_anal_qmf],
                                p_qmf1[4 * (n + 2 * num_band_anal_qmf)]));
      accu = ixheaacd_add64(
          accu, ixheaacd_mult64(inp1[n + 4 * num_band_anal_qmf],
                                p_qmf1[4 * (n + 4 * num_band_anal_qmf)]));
      accu = ixheaacd_add64(
          accu, ixheaacd_mult64(inp1[n + 6 * num_band_anal_qmf],
                                p_qmf1[4 * (n + 6 * num_band_anal_qmf)]));
      accu = ixheaacd_add64(
          accu, ixheaacd_mult64(inp1[n + 8 * num_band_anal_qmf],
                                p_qmf1[4 * (n + 8 * num_band_anal_qmf)]));
      p_out[n] = (WORD32)(accu >> 31);

      accu = ixheaacd_mult64(inp1[n + 1 + 0], p_qmf1[4 * (n + 1 + 0)]);
      accu = ixheaacd_add64(
          accu, ixheaacd_mult64(inp1[n + 1 + 2 * num_band_anal_qmf],
                                p_qmf1[4 * (n + 1 + 2 * num_band_anal_qmf)]));
      accu = ixheaacd_add64(
          accu, ixheaacd_mult64(inp1[n + 1 + 4 * num_band_anal_qmf],
                                p_qmf1[4 * (n + 1 + 4 * num_band_anal_qmf)]));
      accu = ixheaacd_add64(
          accu, ixheaacd_mult64(inp1[n + 1 + 6 * num_band_anal_qmf],
                                p_qmf1[4 * (n + 1 + 6 * num_band_anal_qmf)]));
      accu = ixheaacd_add64(
          accu, ixheaacd_mult64(inp1[n + 1 + 8 * num_band_anal_qmf],
                                p_qmf1[4 * (n + 1 + 8 * num_band_anal_qmf)]));
      p_out[n + 1] = (WORD32)(accu >> 31);

      accu = ixheaacd_mult64(inp2[n + 0], p_qmf2[4 * (n + 0)]);
      accu = ixheaacd_add64(
          accu, ixheaacd_mult64(inp2[n + 2 * num_band_anal_qmf],
                                p_qmf2[4 * (n + 2 * num_band_anal_qmf)]));
      accu = ixheaacd_add64(
          accu, ixheaacd_mult64(inp2[n + 4 * num_band_anal_qmf],
                                p_qmf2[4 * (n + 4 * num_band_anal_qmf)]));
      accu = ixheaacd_add64(
          accu, ixheaacd_mult64(inp2[n + 6 * num_band_anal_qmf],
                                p_qmf2[4 * (n + 6 * num_band_anal_qmf)]));
      accu = ixheaacd_add64(
          accu, ixheaacd_mult64(inp2[n + 8 * num_band_anal_qmf],
                                p_qmf2[4 * (n + 8 * num_band_anal_qmf)]));
      p_out[n + num_band_anal_qmf] = (WORD32)(accu >> 31);

      accu = ixheaacd_mult64(inp2[n + 1 + 0], p_qmf2[4 * (n + 1 + 0)]);
      accu = ixheaacd_add64(
          accu, ixheaacd_mult64(inp2[n + 1 + 2 * num_band_anal_qmf],
                                p_qmf2[4 * (n + 1 + 2 * num_band_anal_qmf)]));
      accu = ixheaacd_add64(
          accu, ixheaacd_mult64(inp2[n + 1 + 4 * num_band_anal_qmf],
                                p_qmf2[4 * (n + 1 + 4 * num_band_anal_qmf)]));
      accu = ixheaacd_add64(
          accu, ixheaacd_mult64(inp2[n + 1 + 6 * num_band_anal_qmf],
                                p_qmf2[4 * (n + 1 + 6 * num_band_anal_qmf)]));
      accu = ixheaacd_add64(
          accu, ixheaacd_mult64(inp2[n + 1 + 8 * num_band_anal_qmf],
                                p_qmf2[4 * (n + 1 + 8 * num_band_anal_qmf)]));
      p_out[n + 1 + num_band_anal_qmf] = (WORD32)(accu >> 31);
    }
  }
}

VOID ixheaacd_esbr_inv_modulation(
    WORD32 *qmf_real, ia_sbr_qmf_filter_bank_struct *syn_qmf,
    ia_qmf_dec_tables_struct *qmf_dec_tables_ptr) {
  ixheaacd_esbr_cos_sin_mod(qmf_real, syn_qmf, qmf_dec_tables_ptr->esbr_w_32,
                            qmf_dec_tables_ptr->dig_rev_table2_32);
}

VOID ixheaacd_sbr_qmfsyn32_winadd(WORD16 *tmp1, WORD16 *tmp2, WORD16 *inp1,
                                  WORD16 *sample_buffer, FLAG shift,
                                  WORD32 ch_fac) {
  WORD32 k;
  WORD32 rounding_fac = 0x8000;
  rounding_fac = rounding_fac >> shift;

  for (k = 0; k < 32; k++) {
    WORD32 syn_out = rounding_fac;

    syn_out = ixheaacd_add32(
        syn_out, ixheaacd_mult16x16in32(tmp1[0 + k], inp1[2 * (k + 0)]));
    syn_out = ixheaacd_add32(
        syn_out, ixheaacd_mult16x16in32(tmp1[128 + k], inp1[2 * (k + 64)]));
    syn_out = ixheaacd_add32(
        syn_out, ixheaacd_mult16x16in32(tmp1[256 + k], inp1[2 * (k + 128)]));
    syn_out = ixheaacd_add32(
        syn_out, ixheaacd_mult16x16in32(tmp1[384 + k], inp1[2 * (k + 192)]));
    syn_out = ixheaacd_add32(
        syn_out, ixheaacd_mult16x16in32(tmp1[512 + k], inp1[2 * (k + 256)]));

    syn_out = ixheaacd_add32(
        syn_out, ixheaacd_mult16x16in32(tmp2[64 + k], inp1[2 * (k + 32)]));
    syn_out = ixheaacd_add32(
        syn_out, ixheaacd_mult16x16in32(tmp2[192 + k], inp1[2 * (k + 96)]));
    syn_out = ixheaacd_add32(
        syn_out, ixheaacd_mult16x16in32(tmp2[320 + k], inp1[2 * (k + 160)]));
    syn_out = ixheaacd_add32(
        syn_out, ixheaacd_mult16x16in32(tmp2[448 + k], inp1[2 * (k + 224)]));
    syn_out = ixheaacd_add32(
        syn_out, ixheaacd_mult16x16in32(tmp2[576 + k], inp1[2 * (k + 288)]));
    syn_out = ixheaacd_add32_sat(syn_out, syn_out);
    if (shift == 2) {
      syn_out = ixheaacd_add32_sat(syn_out, syn_out);
    }
    sample_buffer[ch_fac * k] = (syn_out >> 16);
  }
}

void ixheaacd_sbr_pre_twiddle(WORD32 *p_xre, WORD32 *p_xim,
                              WORD16 *p_twiddles) {
  int k;

  for (k = 62; k >= 0; k--) {
    WORD32 x_re = *p_xre;
    WORD32 x_im = *p_xim;

    WORD16 ixheaacd_cosine = *p_twiddles++;
    WORD16 ixheaacd_sine = *p_twiddles++;

    WORD32 re, im;

    re = ixheaacd_mac32x16in32_shl(
        ixheaacd_mult32x16in32_shl(x_re, ixheaacd_cosine), x_im, ixheaacd_sine);
    im = ixheaacd_sub32(ixheaacd_mult32x16in32_shl(x_im, ixheaacd_cosine),
                        ixheaacd_mult32x16in32_shl(x_re, ixheaacd_sine));

    *p_xre++ = re;
    *p_xim++ = im;
  }
}

VOID ixheaacd_cplx_synt_qmffilt(
    WORD32 **qmf_real, WORD32 **qmf_imag, WORD32 split,
    ia_sbr_scale_fact_struct *sbr_scale_factor, WORD16 *time_out,
    ia_sbr_qmf_filter_bank_struct *qmf_bank, ia_ps_dec_struct *ptr_ps_dec,
    FLAG active, FLAG low_pow_flag, ia_sbr_tables_struct *sbr_tables_ptr,
    ixheaacd_misc_tables *pstr_common_tables, WORD32 ch_fac, FLAG drc_on,
    WORD32 drc_sbr_factors[][64], WORD32 audio_object_type) {
  WORD32 i;

  WORD32 code_scale_factor;
  WORD32 scale_factor;
  WORD32 out_scale_factor;
  WORD32 low_band_scale_factor;
  WORD32 high_band_scale_factor;
  WORD16 *filter_states = qmf_bank->filter_states;
  WORD32 **ptr_qmf_imag_temp;
  WORD32 qmf_real2[2 * NO_SYNTHESIS_CHANNELS];

  WORD32 no_synthesis_channels = qmf_bank->no_channels;
  WORD32 p1;

  WORD16 *fp1;
  WORD16 *fp2;

  WORD32 sixty4 = NO_SYNTHESIS_CHANNELS;
  WORD32 thirty2 = qmf_bank->no_channels;

  WORD16 *filter_coeff;
  WORD32 num_time_slots = qmf_bank->num_time_slots;
  WORD32 ixheaacd_drc_offset;
  WORD32 ov_lb_scale = sbr_scale_factor->ov_lb_scale;
  WORD32 lb_scale = sbr_scale_factor->lb_scale;
  WORD32 st_syn_scale = sbr_scale_factor->st_syn_scale;
  WORD32 ov_lb_shift, lb_shift, hb_shift;

  WORD32 *qmf_real_tmp = qmf_real2;
  WORD32 *qmf_imag_tmp = &qmf_real2[NO_SYNTHESIS_CHANNELS];
  WORD32 env = 0;

  WORD32 common_shift;

  if (no_synthesis_channels == 32) {
    qmf_bank->cos_twiddle =
        (WORD16 *)sbr_tables_ptr->qmf_dec_tables_ptr->sbr_sin_cos_twiddle_l32;
    qmf_bank->alt_sin_twiddle =
        (WORD16 *)sbr_tables_ptr->qmf_dec_tables_ptr->sbr_alt_sin_twiddle_l32;
    qmf_bank->t_cos =
        (WORD16 *)
            sbr_tables_ptr->qmf_dec_tables_ptr->sbr_cos_sin_twiddle_ds_l32;
  } else {
    qmf_bank->cos_twiddle =
        (WORD16 *)sbr_tables_ptr->qmf_dec_tables_ptr->sbr_sin_cos_twiddle_l64;
    qmf_bank->alt_sin_twiddle =
        (WORD16 *)sbr_tables_ptr->qmf_dec_tables_ptr->sbr_alt_sin_twiddle_l64;
  }
  if (audio_object_type != AOT_ER_AAC_ELD &&
      audio_object_type != AOT_ER_AAC_LD) {
    qmf_bank->filter_pos_syn +=
        (sbr_tables_ptr->qmf_dec_tables_ptr->qmf_c - qmf_bank->p_filter);
    qmf_bank->p_filter = sbr_tables_ptr->qmf_dec_tables_ptr->qmf_c;
  } else {
    qmf_bank->filter_pos_syn +=
        (sbr_tables_ptr->qmf_dec_tables_ptr->qmf_c_eld - qmf_bank->p_filter);
    qmf_bank->p_filter = sbr_tables_ptr->qmf_dec_tables_ptr->qmf_c_eld;
  }

  fp1 = &filter_states[0];
  fp2 = fp1 + no_synthesis_channels;

  if (audio_object_type == AOT_ER_AAC_ELD ||
      audio_object_type == AOT_ER_AAC_LD) {
    fp1 = qmf_bank->fp1_syn;
    fp2 = qmf_bank->fp2_syn;
    sixty4 = qmf_bank->sixty4;
  }

  filter_coeff = qmf_bank->filter_pos_syn;

  if (active) {
    code_scale_factor = scale_factor = sbr_scale_factor->ps_scale;
  } else {
    code_scale_factor = ixheaacd_min32(lb_scale, ov_lb_scale);
    scale_factor = sbr_scale_factor->hb_scale;
  }

  low_band_scale_factor = (st_syn_scale - code_scale_factor);
  high_band_scale_factor = (st_syn_scale - scale_factor);

  p1 = 0;

  if (low_pow_flag)

  {
    ov_lb_shift = (st_syn_scale - ov_lb_scale) - 4;
    lb_shift = (st_syn_scale - lb_scale) - 4;
    hb_shift = high_band_scale_factor - 4;
    out_scale_factor = -((sbr_scale_factor->st_syn_scale - 1));
    ptr_qmf_imag_temp = 0;

  }

  else {
    out_scale_factor = -((sbr_scale_factor->st_syn_scale - 3));
    if (active) {
      ov_lb_shift = (sbr_scale_factor->ps_scale - ov_lb_scale);
      lb_shift = (sbr_scale_factor->ps_scale - lb_scale);
      hb_shift = (sbr_scale_factor->ps_scale - sbr_scale_factor->hb_scale);
      common_shift = low_band_scale_factor - 8;

    } else {
      if (audio_object_type != AOT_ER_AAC_ELD &&
          audio_object_type != AOT_ER_AAC_LD) {
        ov_lb_shift = (st_syn_scale - ov_lb_scale) - 8;
        lb_shift = (st_syn_scale - lb_scale) - 8;
        hb_shift = high_band_scale_factor - 8;
      } else {
        ov_lb_shift = (st_syn_scale - ov_lb_scale) - 7;
        lb_shift = (st_syn_scale - lb_scale) - 7;
        hb_shift = high_band_scale_factor - 7;
      }
      common_shift = 0;
    }
    ptr_qmf_imag_temp = qmf_imag;
  }

  {
    if (ov_lb_shift == lb_shift) {
      (*ixheaacd_adjust_scale)(qmf_real, ptr_qmf_imag_temp, 0, qmf_bank->lsb, 0,
                               num_time_slots, ov_lb_shift, low_pow_flag);

    } else {
      (*ixheaacd_adjust_scale)(qmf_real, ptr_qmf_imag_temp, 0, qmf_bank->lsb, 0,
                               split, ov_lb_shift, low_pow_flag);

      (*ixheaacd_adjust_scale)(qmf_real, ptr_qmf_imag_temp, 0, qmf_bank->lsb,
                               split, num_time_slots, lb_shift, low_pow_flag);
    }

    (*ixheaacd_adjust_scale)(qmf_real, ptr_qmf_imag_temp, qmf_bank->lsb,
                             qmf_bank->usb, 0, num_time_slots, hb_shift,
                             low_pow_flag);
  }

  ixheaacd_drc_offset = qmf_bank->ixheaacd_drc_offset;

  if (1 == drc_on) {
    for (i = 0; i < num_time_slots; i++) {
      WORD32 loop_val;
      for (loop_val = 0; loop_val < 64; loop_val++) {
        qmf_real[i][loop_val] = ixheaacd_mult32x32in32_shift25(
            qmf_real[i][loop_val], drc_sbr_factors[6 + i][loop_val]);
      }
    }
  }

  if (low_pow_flag)

  {
    WORD16 *fptemp;

    VOID(*sbr_qmf_syn_winadd)
    (WORD16 *, WORD16 *, WORD16 *, WORD16 *, FLAG, WORD32);
    ia_qmf_dec_tables_struct *qmf_tab_ptr = sbr_tables_ptr->qmf_dec_tables_ptr;

    if (no_synthesis_channels == NO_SYNTHESIS_CHANNELS_DOWN_SAMPLED)
      sbr_qmf_syn_winadd = ixheaacd_sbr_qmfsyn32_winadd;
    else
      sbr_qmf_syn_winadd = ixheaacd_sbr_qmfsyn64_winadd;

    for (i = 0; i < num_time_slots; i++) {
      ixheaacd_inv_modulation_lp(qmf_real[i],
                                 &filter_states[ixheaacd_drc_offset], qmf_bank,
                                 qmf_tab_ptr);

      sbr_qmf_syn_winadd(fp1, fp2, filter_coeff, &time_out[ch_fac * p1], 2,
                         ch_fac);

      ixheaacd_drc_offset -= no_synthesis_channels << 1;

      if (ixheaacd_drc_offset < 0)
        ixheaacd_drc_offset += ((no_synthesis_channels << 1) * 10);

      fptemp = fp1;
      fp1 = fp2;
      fp2 = fptemp;

      filter_coeff += 64;

      if (filter_coeff == qmf_bank->p_filter + 640)
        filter_coeff = (WORD16 *)qmf_bank->p_filter;

      p1 += no_synthesis_channels;
    }

  } else {
    for (i = 0; i < num_time_slots; i++) {
      WORD32 *t_qmf_imag;
      t_qmf_imag = qmf_imag[i];

      if (active) {
        if (i == ptr_ps_dec->border_position[env]) {
          ixheaacd_init_rot_env(ptr_ps_dec, (WORD16)env, qmf_bank->usb,
                                sbr_tables_ptr, pstr_common_tables->trig_data);
          env++;
        }

        ixheaacd_apply_ps(ptr_ps_dec, &qmf_real[i], &qmf_imag[i], qmf_real_tmp,
                          qmf_imag_tmp, sbr_scale_factor, (WORD16)i,
                          sbr_tables_ptr);
      }
      if (1 == drc_on) {
        WORD32 loop_val;
        for (loop_val = 0; loop_val < 64; loop_val++) {
          qmf_real[i][loop_val] = ixheaacd_mult32x32in32_shift25(
              qmf_real[i][loop_val], drc_sbr_factors[6 + i][loop_val]);
        }
      }

      if (active) {
        if (common_shift)
          ixheaacd_shiftrountine(qmf_real[i], t_qmf_imag, no_synthesis_channels,
                                 common_shift);
      }

      if (audio_object_type == AOT_ER_AAC_ELD ||
          audio_object_type == AOT_ER_AAC_LD)
        ixheaacd_sbr_pre_twiddle(
            qmf_real[i], t_qmf_imag,
            sbr_tables_ptr->qmf_dec_tables_ptr->ixheaacd_sbr_synth_cos_sin_l32);

      ixheaacd_inv_emodulation(qmf_real[i], qmf_bank,
                               sbr_tables_ptr->qmf_dec_tables_ptr);

      {
        WORD32 temp_out_scale_fac = out_scale_factor + 1;
        if (audio_object_type == AOT_ER_AAC_LD ||
            audio_object_type == AOT_ER_AAC_ELD) {
          temp_out_scale_fac = temp_out_scale_fac - 1;

          ixheaacd_shiftrountine_with_rnd_eld(
              qmf_real[i], t_qmf_imag, &filter_states[ixheaacd_drc_offset],
              no_synthesis_channels, temp_out_scale_fac);

        }

        else {
          ixheaacd_shiftrountine_with_rnd(
              qmf_real[i], t_qmf_imag, &filter_states[ixheaacd_drc_offset],
              no_synthesis_channels, temp_out_scale_fac);
        }
      }

      if (no_synthesis_channels == NO_SYNTHESIS_CHANNELS_DOWN_SAMPLED) {
        WORD32 temp = 1;
        if (audio_object_type == AOT_ER_AAC_LD ||
            audio_object_type == AOT_ER_AAC_ELD) {
          temp = 2;
        }
        ixheaacd_sbr_qmfsyn32_winadd(fp1, fp2, filter_coeff,
                                     &time_out[ch_fac * p1], temp, ch_fac);

        fp1 += thirty2;
        fp2 -= thirty2;
        thirty2 = -thirty2;

        ixheaacd_drc_offset -= 64;

        if (ixheaacd_drc_offset < 0) ixheaacd_drc_offset += 640;

      } else {
        WORD32 temp = 1;
        if (audio_object_type == AOT_ER_AAC_LD ||
            audio_object_type == AOT_ER_AAC_ELD) {
          temp = 2;
        }
        ixheaacd_sbr_qmfsyn64_winadd(fp1, fp2, filter_coeff,
                                     &time_out[ch_fac * p1], temp, ch_fac);

        fp1 += sixty4;
        fp2 -= sixty4;
        sixty4 = -sixty4;
        ixheaacd_drc_offset -= 128;

        if (ixheaacd_drc_offset < 0) ixheaacd_drc_offset += 1280;
      }

      filter_coeff += 64;

      if (filter_coeff == qmf_bank->p_filter + 640)
        filter_coeff = (WORD16 *)qmf_bank->p_filter;

      p1 += no_synthesis_channels;

      if (active)
        memcpy(qmf_real[i], qmf_real_tmp,
               2 * no_synthesis_channels * sizeof(WORD32));
    }
  }

  if (audio_object_type == AOT_ER_AAC_LD ||
      audio_object_type == AOT_ER_AAC_ELD) {
    qmf_bank->fp1_syn = fp1;
    qmf_bank->fp2_syn = fp2;
    qmf_bank->sixty4 = sixty4;
  }

  qmf_bank->filter_pos_syn = filter_coeff;
  qmf_bank->ixheaacd_drc_offset = ixheaacd_drc_offset;
}
