/**********************************************************************
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 "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[50]={0,
	  4,   8,  12,  16,  20,  
	 24,  28,  32,  36,  40,
	 48,  56,  64,  72,  80, 
     88,  96, 108, 120, 132, 
	144, 160, 176, 196, 216, 
	240, 264, 292, 320, 352, 
	384, 416, 448, 480, 512, 
	544, 576, 608, 640, 672, 
	704, 736, 768, 800, 832, 
	864, 896, 928, 1024
};

static int	swb_offset_short[15]={0,
	 4, 8, 12, 16, 20, 28, 36, 44, 56, 68, 80, 96, 112, 128
};

static Float sam_calc_scale(int fac);
static Float sam_iquant_exp(int q);

static void sam_reshuffle(int maxSfb, int samples[]);

static Float  exptable[TEXP];
static Float  iq_exp_tbl[MAX_IQ_TBL];

static int	index2cband[2][1024];

int sam_decode_bsac_data(int target,
		int	stereo_mode,
		int	windowSequence[],
		int scalefactors[][112],
		int groupInfo[][7],
		int samples[][1024],
		int	maxSfb[],
		int maxScalefactor[],
		int	ms_mask[],
		int	is_info[],
		int	nch)
{
	int	i, j, k, b, w, width;
	int	cband, qband;
	int	ch;
	int	fsize_len;
	int	frameLength;
	int	encodedLayer;
	int	num_window_group[2];
	int	swb_offset[2][114];
	int	startB[8], endB[8];
	int	tempband0[1024];

	if(nch == 1)
		fsize_len = 9;
	else if(nch == 2)
		fsize_len = 10;

	frameLength = sam_getbits(fsize_len) * 8;
	encodedLayer = sam_getbits(6);

	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 < 14; 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[128*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[128*b+i+k];
				}
			}

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

	sam_decode_bsac_stream(windowSequence, encodedLayer, target, samples,
		maxScalefactor, scalefactors, maxSfb, groupInfo, ms_mask, stereo_mode, is_info,
		swb_offset, frameLength, nch);

	return frameLength;
}

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++) {
				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++)
					decSpectrum[b*128+i] = sam_iquant_exp(samples[b*128+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++, j++)
				tmpSample[128*b+i+k] = samples[j];

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

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

			sign_sect = (is_info[sfb] == 1) ? 1 : -1;
			pred[0][7+sfb] = pred[1][7+sfb] = 0;

			if(ms_mask[0])
				sign_sfb = sign_sect * (ms_mask[sfb+1] == 0) ? 1 : -1;
			else 
				sign_sfb = sign_sect;
		
			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;

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

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

				if(ms_mask[0])
					sign_sfb = sign_sect * (ms_mask[band+1] == 0) ? 1 : -1;
				else 
					sign_sfb = sign_sect;
		
				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_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*128+i];
					r = spectrum[1][b*128+i];

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

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

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*128), 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);
}

static Float sam_calc_scale(int fac)
{
	Float	scale;

	if(fac >= 0 && fac < TEXP) {
		scale = 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)iq_exp_tbl[q]);
		}
		else {
	    	return_value = (Float)(pow((double)q, (double)(4./3.)));
		}
    }
    else {
		q = -q;
		if (q < MAX_IQ_TBL) {
	    	return((Float)(-iq_exp_tbl[q]));
		}
		else {
	    	return_value = (Float)(-pow((double)q, (double)(4./3.)));
		}
    }

	return return_value;
}

void sam_decode_init(void)
{
	int	i;

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

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

	sam_predinit();
}

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 numUsedBits;

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

long sam_getbits(int n)
{
  unsigned long value;
  if (n!=0){
    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 numUsedBits;
}
