/**********************************************************************
MPEG-4 Audio VM

This software module was originally developed by
  Y.B. Thomas Kim and S.H. Park (Samsung AIT)
and edited by
  Y.B. Thomas Kim (Samsung AIT) on 1997-11-06

in the course of development of the MPEG-2 NBC/MPEG-4 Audio standard
ISO/IEC 13818-7, 14496-1,2 and 3. This software module is an
implementation of a part of one or more MPEG-2 NBC/MPEG-4 Audio tools
as specified by the MPEG-2 NBC/MPEG-4 Audio standard. ISO/IEC gives
users of the MPEG-2 NBC/MPEG-4 Audio standards free license to this
software module or modifications thereof for use in hardware or
software products claiming conformance to the MPEG-2 NBC/ MPEG-4 Audio
standards. Those intending to use this software module in hardware or
software products are advised that this use may infringe existing
patents. The original developer of this software module and his/her
company, the subsequent editors and their companies, and ISO/IEC have
no liability for use of this software module or modifications thereof
in an implementation. Copyright is not released for non MPEG-2
NBC/MPEG-4 Audio conforming products. The original developer retains
full right to use the code for his/her own purpose, assign or donate
the code to a third party and to inhibit third party from using the
code for non MPEG-2 NBC/MPEG-4 Audio conforming products. This
copyright notice must be included in all copies or derivative works.

Copyright (c) 1997.

**********************************************************************/
#include <stdio.h>
#include <math.h>
#include "all.h"
#include "common.h"
#include "bitstream.h"
#include "tf_main.h"
#include "sam_dec.h"

#define SF_OFFSET   100
#define TEXP    128
#define MAX_IQ_TBL  128

static int swb_offset_long[52];
static int swb_offset_short[16];

static int      long_block_size;
static int      short_block_size;
static int      long_sfb_top;
static int      short_sfb_top;
static int      fs_idx;

static Float sam_calc_scale(int fac);
static Float sam_iquant_exp(int q);
static void sam_gen_rand_vector(float *spec, int size, long *state);

static void sam_reshuffle(int maxSfb, int samples[]);
static long sam_random2( long *seed );

static Float  sam_exptable[TEXP];
static Float  sam_iq_exp_tbl[MAX_IQ_TBL];

static int      index2cband[2][1024];
extern int    sam_profile;

void sam_decode_bsac_data(int target,
       int    frameLength,
       int    encodedLayer,
       int    core_used_bits,
       int    stereo_mode,
       WINDOW_TYPE  windowSequence[],
       int    scalefactors[][136],
       int    groupInfo[][7],
       int    samples[][1024],
       int    maxSfb[],
       int    ms_mask[],
       int    is_info[],
       int    *pns_sfb_start,
       int    pns_sfb_flag[][136],
       int    pns_sfb_mode[],
       int    block_size_samples,
       int    nch)
{
  int     i, j, k, b, w, width;
  int     cband, qband;
  int     ch;
  int     num_window_group[2];
  int     swb_offset[2][136];
  int     startB[8], endB[8];
  int     tempband0[1024];

  target = (target - 16);

  for(ch = 0; ch < nch; ch++) {

    if(windowSequence[ch] == EIGHT_SHORT_SEQUENCE) {
      num_window_group[ch] = sam_get_region_info(groupInfo[ch], startB, endB);
      b = 0;
      swb_offset[ch][b] = 0;
      b = 1;
      for(i = 0; i < short_sfb_top; i++) {
        for(w = 0; w < num_window_group[ch]; w++, b++) {
          width = (swb_offset_short[i+1] - swb_offset_short[i]) * (endB[w]-startB[w]);
          swb_offset[ch][b] = swb_offset[ch][b-1] + width;
        }
      }

      for(i = 0;i < 1024; i++) {
        tempband0[i] = -1;
        index2cband[ch][i] = -1;
      }

    for(qband = 0; qband < maxSfb[ch]; qband++)  {
      for (i = swb_offset_short[qband]; i < swb_offset_short[qband+1]; i+=4) 
      for(w = 0; w < num_window_group[ch]; w++)  {
          cband = i*(endB[w]-startB[w])/32;
          for (b = startB[w]; b < endB[w]; b++) {
            for (k=0; k<4; k++) {
              tempband0[short_block_size*b+i+k] = 24*w+cband;
            }
          }
        }
      }

      j = 0;
      for(i = 0; i < swb_offset_short[maxSfb[ch]]; i+=4) {
        for(b = 0; b < 8; b++) {
          for(k = 0; k < 4; k++, j++)
            index2cband[ch][j] = tempband0[short_block_size*b+i+k];
        }
      }

    } else {
      num_window_group[ch] = 1;
      for(i = 0; i < long_sfb_top+1; i++)
        swb_offset[ch][i] = swb_offset_long[i];
    }
  }

  sam_decode_bsac_stream(windowSequence, 
             target,
                 frameLength,
                 encodedLayer,
             core_used_bits,
             samples,
             scalefactors, 
             maxSfb, 
             groupInfo, 
             ms_mask, 
             stereo_mode, 
             is_info, 
                 pns_sfb_start,
                 pns_sfb_flag,
                 pns_sfb_mode,
             swb_offset, 
                 block_size_samples,
                 nch);

}

int sam_i2cb(int ch, int i)
{
  return index2cband[ch][i];
}

void sam_dequantization(int target,
    int     windowSequence,
    int scalefactors[],
    int groupInfo[],
    int samples[],
    int     maxSfb,
    int     is_info[],
    Float decSpectrum[],
    int ch)
{
  int     sfb, i;
  Float   scale;

  target = (target - 16);

  for(i = 0; i < 1024; i++)
    decSpectrum[i] = -0.0;

  if(windowSequence != EIGHT_SHORT_SEQUENCE) {

    for(sfb = 0; sfb < maxSfb; sfb++) {
      if(ch == 1 && is_info[sfb]) continue;

      scale = sam_calc_scale(scalefactors[sfb]-SF_OFFSET);

      for(i = swb_offset_long[sfb]; i < swb_offset_long[sfb+1]; i++) {
        if (samples[i])
          decSpectrum[i] = sam_iquant_exp(samples[i]) * scale;
      }
    }

  } else {
    int w, b, num_window_group;
    int startB[8], endB[8];

    num_window_group = sam_get_region_info(groupInfo, startB, endB);

    sam_reshuffle(maxSfb, samples);

    for(w = 0; w < num_window_group; w++) {
      for(sfb = 0; sfb < maxSfb; sfb++) {
        if(ch == 1 && is_info[(w*maxSfb)+sfb]) continue;

        scale = sam_calc_scale(scalefactors[(w*maxSfb)+sfb]-SF_OFFSET);

        for(b = startB[w] ; b < endB[w]; b++)
        for(i = swb_offset_short[sfb]; i < swb_offset_short[sfb+1]; i++)
          if (samples[b*short_block_size+i])
          decSpectrum[b*short_block_size+i] = sam_iquant_exp(samples[b*short_block_size+i]) * scale;
      }
    }
  }
}

static void sam_reshuffle(int maxSfb, int samples[])
{
  int     i, j, k, b;
  int     tmpSample[1024];

  for(i = 0; i < 1024; i++)
    tmpSample[i] = 0;

  j = 0;
  for(i = 0; i < swb_offset_short[maxSfb]; i+=4)
    for(b = 0; b < 8; b++)
      for(k = 0; k < 4; k++)
        tmpSample[short_block_size*b+i+k] = samples[j++];

  for(i = 0; i < long_block_size; i++)
    samples[i] = tmpSample[i];
}

void sam_ms_stereo(int windowSequence,
    int groupInfo[],
    Float spectrum[][1024],
    int ms_mask[],
    int maxSfb)
{
  Float   l, r, tmp;
  int     sfb, i;

  if(windowSequence != EIGHT_SHORT_SEQUENCE) {
    for(sfb = 0; sfb < maxSfb; sfb++) {
      if(ms_mask[sfb+1] == 0) continue;
      for(i = swb_offset_long[sfb]; i < swb_offset_long[sfb+1]; i++) {
        l = spectrum[0][i];
        r = spectrum[1][i];

        tmp = l + r;
        r = l - r;
        l = tmp;

        spectrum[0][i] = l;
        spectrum[1][i] = r;
      }
    }
  } else {
    int w, b, num_window_group;
    int startB[8], endB[8];

    num_window_group = sam_get_region_info(groupInfo, startB, endB);

    for(w = 0; w < num_window_group; w++) {
      for(b = startB[w] ; b < endB[w]; b++)
      for(sfb = 0; sfb < maxSfb; sfb++) {
        if(ms_mask[w*maxSfb+sfb+1]==0)continue;
        for(i = swb_offset_short[sfb]; i < swb_offset_short[sfb+1]; i++) {
          l = spectrum[0][b*short_block_size+i];
          r = spectrum[1][b*short_block_size+i];

          tmp = l + r;
          r = l - r;
          l = tmp;

          spectrum[0][b*short_block_size+i] = l;
          spectrum[1][b*short_block_size+i] = r;
        }
      }
    }
  }
}

/*
 *  for PNS
 */
#define	MEAN_NRG 1.5625e+18 /* Theory: (2^31)^2 / 3 = 1.537228e+18 */
static long sam_random2( long *seed )
{
  *seed = (1664525L * *seed) + 1013904223L; /* Numerical recipes */
  return(long)(*seed);
}

static void sam_gen_rand_vector(float *spec, int size, long *state)
{
  int i;
  float s, norm, nrg=0.0;

  norm = 1.0 / sqrt(size * MEAN_NRG);

  for(i=0; i<size; i++) {
    spec[i] = (float)(sam_random2(state) * norm);
    nrg += spec[i] * spec[i];
  }
  s = 1.0 / sqrt( nrg );
  for(i=0; i<size; i++)
    spec[i] *= s;
}

void sam_pns(int windowSequence,
    int     groupInfo[],
    int     maxSfb,
    int     pns_sfb_flag[][136],
    int     pns_sfb_mode[],
    Float   spectrums[][1024],
    int     factors[][136],
    int     predict_data[2][60],
    int     nch)
{
  int    i, size, ch, sfb;
  int    corr_flag;
  long   *nsp;
  Float  *fp, scale;
  static long cur_noise_state;
  static long noise_state_save[136];
  static long lp_store[136];
  static int cnt=0;

  for(ch = 0; ch < nch; ch++) {
    nsp = noise_state_save;
    for(sfb = 0; sfb < maxSfb; sfb++) {
      if(pns_sfb_flag[ch][sfb] == 0)
        continue;
      predict_data[ch][7+sfb] = 0;
      corr_flag = pns_sfb_mode[sfb];
      fp = spectrums[ch]+swb_offset_long[sfb];
      size = swb_offset_long[sfb+1]-swb_offset_long[sfb];
      if(corr_flag) {
        sam_gen_rand_vector(fp, size, nsp+sfb);
      } else {
        nsp[sfb] = cur_noise_state;
        sam_gen_rand_vector(fp, size, &cur_noise_state);
      }
      scale = pow(2.0, 0.25*factors[ch][sfb]);
      for(i = swb_offset_long[sfb]; i < swb_offset_long[sfb+1]; i++)
        *fp++ *= scale;
    }
  }
}

void sam_intensity(int windowSequence,
    int     groupInfo[],
    Float spectrum[][1024],
    int     factors[][136],
    int is_info[], 
    int     ms_mask[],
    int     pred[][60],
    int     maxSfb)
{
  int     i, sfb;
  int     sign_sfb;
  Float   scale;
  if(windowSequence != EIGHT_SHORT_SEQUENCE) {
    for(sfb = 0; sfb < maxSfb; sfb++) {
      if(is_info[sfb] == 0) continue;

      pred[0][7+sfb] = pred[1][7+sfb] = 0;

      sign_sfb = (is_info[sfb] == 1) ? 1 : -1;

      scale = sign_sfb * pow( 0.5, 0.25*factors[1][sfb]);

      for(i = swb_offset_long[sfb]; i < swb_offset_long[sfb+1]; i++) {
        spectrum[1][i] = spectrum[0][i] * scale;
      }
    }
  } else {
    int w, b, band, num_window_group;
    int startB[8], endB[8];

    num_window_group = sam_get_region_info(groupInfo, startB, endB);

    for(w = 0; w < num_window_group; w++) {
      for(b = startB[w] ; b < endB[w]; b++)
      for(sfb = 0; sfb < maxSfb; sfb++) {
        band = (w*maxSfb)+sfb;
        if(is_info[band]==0)continue;

        pred[0][7+band] = pred[1][7+band] = 0;

        sign_sfb = (is_info[band] == 1) ? 1 : -1;

        scale = sign_sfb * pow( 0.5, 0.25*factors[1][band]);

        for(i = swb_offset_short[sfb]; i < swb_offset_short[sfb+1]; i++)
          spectrum[1][i] = spectrum[0][i] * scale;
      }
    }
  }
}

void
sam_tns_data(int windowSequence, int maxSfb, Float spect[1024], sam_TNS_frame_info *tns)
{
  int     i;
  int     blocks;
  int     *sfbs;

  if(windowSequence == 2) {
    sfbs = (int *)swb_offset_short;
    blocks = 8;
  } else {
    sfbs = (int *)swb_offset_long;
    blocks = 1;
  }

  for(i = 0; i < blocks; i++) {
    sam_tns_decode_subblock(windowSequence, spect+(i*short_block_size), maxSfb, sfbs, &(tns->info[i]));
  }
}

void
sam_prediction(int windowSequence, int lpflag[], Float coef[], int ch)
{
    if (windowSequence == 2)
    return;

  sam_predict(windowSequence, lpflag, coef, swb_offset_long, ch);
}

void
sam_predict_pns_reset(int windowSequence, int maxSfb, int *pns_sfb_flag, int ch)
{
    if (windowSequence == 2)
    return;

	sam_predict_pns_reset1(maxSfb, pns_sfb_flag, swb_offset_long, ch);
}

static Float sam_calc_scale(int fac)
{
  Float   scale;

  if(fac >= 0 && fac < TEXP) {
    scale = sam_exptable[fac];
  } else {
    if(fac == -SF_OFFSET)
      scale = 0.;
    else
      scale = pow((double)2.0, (double)(0.25*fac));
  }

  return scale;
}

static Float sam_iquant_exp(int q)
{
  Float return_value;

    if (q > 0) {
    if (q < MAX_IQ_TBL) {
    return((Float)sam_iq_exp_tbl[q]);
    }
    else {
    return_value = (Float)(pow((double)q, (double)(4./3.)));
    }
    }
    else {
    q = -q;
    if (q < MAX_IQ_TBL) {
    return((Float)(-sam_iq_exp_tbl[q]));
    }
    else {
    return_value = (Float)(-pow((double)q, (double)(4./3.)));
    }
    }

  return return_value;
}

void sam_decode_init(int sampling_rate_decoded, int block_size_samples)
{
  int     i;

  for(i = 0; i < TEXP; i++)
    sam_exptable[i] = (Float)pow((double)2.0, (double)(0.25*i));

  for(i = 0; i < MAX_IQ_TBL; i++)
    sam_iq_exp_tbl[i] = (Float)pow((double)i, (double)4./3.);

  sam_predinit();
  sam_scale_bits_init(sampling_rate_decoded, block_size_samples);

  long_block_size = block_size_samples;
  short_block_size = long_block_size / 8;

  fs_idx = Fs_48;
  for(i = 0; i < (1<<LEN_SAMP_IDX); i++) {
    if(sampling_rate_decoded == samp_rate_info[i].samp_rate) {
      fs_idx = i;
      break;
    }
  }

  if(block_size_samples == 1024) {
    long_sfb_top = samp_rate_info[fs_idx].nsfb1024;
    short_sfb_top = samp_rate_info[fs_idx].nsfb128;
    swb_offset_long[0] = 0;
    for(i = 0; i < long_sfb_top; i++)
      swb_offset_long[i+1] = samp_rate_info[fs_idx].SFbands1024[i];
    swb_offset_short[0] = 0;
    for(i = 0; i < short_sfb_top; i++)
      swb_offset_short[i+1] = samp_rate_info[fs_idx].SFbands128[i];
  } else if(block_size_samples == 960) {
    long_sfb_top = samp_rate_info[fs_idx].nsfb960;
    short_sfb_top = samp_rate_info[fs_idx].nsfb120;
    swb_offset_long[0] = 0;
    for(i = 0; i < long_sfb_top; i++)
      swb_offset_long[i+1] = samp_rate_info[fs_idx].SFbands960[i];
    swb_offset_short[0] = 0;
    for(i = 0; i < short_sfb_top; i++)
      swb_offset_short[i+1] = samp_rate_info[fs_idx].SFbands120[i];
  }
}

int sam_pred_max_bands()
{
  return pred_max_bands_tbl[fs_idx];
}

int sam_tns_max_bands(int windowSequence)
{
  int idx=0;
  if(windowSequence == 2) idx = 1;
  if(sam_profile == 2) idx += 2;

  return tns_max_bands_tbl[fs_idx][idx];
}

int sam_tns_max_order(int windowSequence)
{
  if(windowSequence == 2) return 7;

  if(sam_profile == 0) return 20;
  else return 12;
}

int sam_get_region_info(int *groupInfo,
  int *startRegion,
  int *endRegion)
{
  int     b, reg;
  int     num_window_group;

  reg = 0;
  startRegion[0] = 0;
  for (b=0; b<7; b++) {
    if (groupInfo[b]==0) {
      startRegion[reg+1] = endRegion[reg] = b+1;
      reg++;
    }
  }
  endRegion[reg] = 8;
  num_window_group = reg + 1;

  return num_window_group;
}

/*
 *  for BISTREAM
 */

static BsBitStream *vmBitBuffer;
static sam_numUsedBits;

void sam_setBsacdec2BitBuffer(BsBitStream *fixed_stream)
{
  vmBitBuffer = fixed_stream;
  sam_numUsedBits = 0;
}

long sam_GetBits(int n)
{
  unsigned long value;
  if (n!=0){
    sam_numUsedBits+=n;
    BsGetBit(vmBitBuffer,&value,n);

  /*
    if (debug['b'])
      PRINT(SE, "  AAC: GetBits: val=%5ld      num=%5d\n", value, n );
  */
  }
  return value;
}

int
sam_getUsedBits(void)
{
  return sam_numUsedBits;
}
