/*******************************************************
*           SoundBooster_DRC_classic.cpp               *
********************************************************/


//#include <string.h>

#include "SoundBooster_DRC_classic_impl.h"
#include "ssc_macro.h"
#include "config.h"


#ifndef HW_FIXED
#include <math.h>
#else
#ifdef KHW_RMS_COMPENSATE
#include "rc_intrinsic.h"
#endif
#endif

#ifndef KHW_RMS_MONO32_FIXED
#include <math.h>
#endif



#ifdef KHW_DEBUG_COUNT
extern int count;
#endif


#ifdef KHW_DEBUG_COUNT
extern int error_check_flag;
#endif

#define NOISEBITS 0

#ifdef USEASM
#define DRCCLASSIC_USEASM
#endif



/////////////////////////////////////////////////
DRC_T drc_struct;
int Soundbooster_DRC_Fs;
int DRC_ChNum;
MODE_T DRC_ModeFlag;
/////////////////////////////////////////////////


/// !!! TBD : replace by optimized functions !!! MIPS

#ifndef KHW_DRC_DETECTION_RULE

static int SoundBooster_DRC_classic_maxabs(const int* in, int n)
{
	int maxabs=0;

	do{
		int data=(*in++);
		if (data<0) data=-data;
		if (maxabs<data) 
			maxabs=data;

	}while(--n>0);

	return maxabs;
}

#else


//static __inline int DVTXOP_L_abs(int L_var1) {return AE_ABS32S_scalar(L_var1);}
static int SoundBooster_DRC_classic_maxabs(const int* in, int n, int ref_max_num, int *ret_max_count)
{

#ifndef DRC_HIFIx
    int maxabs=0;
	int max_count = 0;

    do{
        int data=(*in++);
        if (data<0) 
			data=-data;

        if (maxabs<data) 
			maxabs=data;

		if (data>ref_max_num) 
			max_count++;

    }while(--n>0);

	*ret_max_count = max_count;
    return maxabs;
#else

/////////////////////////  ONLY For Even Number n ////////////////////////////////////////
/*
	int i;
	ae_int32x2 refv, onev, countv, zerov, maxv,datav;
	ae_int32x2* ptx;
	ae_valign align1;
	int max1, max2, count1, count2;
	xtbool2 bool2;

	maxv = AE_MOV32(0);
	refv = AE_MOV32(ref_max_num);
	onev = AE_MOV32(1);
	countv = AE_MOV32(0);

	ptx = (ae_int32x2*)in;
	align1 = AE_LA64_PP(ptx);
	for (i=0;i<n;i+=2)
	{
		AE_LA32X2_IP(datav,align1, ptx);
		maxv = AE_MAXABS32S(maxv, datav);
		zerov = AE_MOV32(0);
		bool2 = AE_LT32(refv, AE_ABS32S(datav));
		AE_MOVT32X2(zerov, onev, bool2);
		countv = AE_ADD32(countv, zerov);
	}
	max1 = AE_MOVAD32_H(maxv);
	max2 = AE_MOVAD32_L(maxv);

	count1 = AE_MOVAD32_H(countv);
	count2 = AE_MOVAD32_L(countv);

	*ret_max_count = count1 + count2;
	return MAX32(max1, max2);
*/

/////////////////////////////////////////////////////////////////////////////////////
		int i;
		int nRemain;

		int odd_num = (n&1);

		ae_int32x2 refv, onev, countv, zerov, maxv,datav;
		ae_int32x2* ptx;
		ae_valign align1;
		int max1, max2, count1, count2, max, count;
		xtbool2 bool2;

		maxv = AE_MOV32(0);
		refv = AE_MOV32(ref_max_num);
		onev = AE_MOV32(1);
		countv = AE_MOV32(0);
		nRemain = n&0x3;
		ptx = (ae_int32x2*)in;
		align1 = AE_LA64_PP(ptx);




		for (i=0;i<n-odd_num;i+=2)
		{
			AE_LA32X2_IP(datav,align1, ptx);
			maxv = AE_MAXABS32S(maxv, datav);
			zerov = AE_MOV32(0);
			bool2 = AE_LT32(refv, AE_ABS32S(datav));
			AE_MOVT32X2(zerov, onev, bool2);
			countv = AE_ADD32(countv, zerov);
		}

/*
		if(error_check_flag == 1)
		{
			printf("i = %d, n=%d",i,n);
		}
*/


		max1 = AE_MOVAD32_H(maxv);
		max2 = AE_MOVAD32_L(maxv);

/*
		if(error_check_flag == 1)
		{
			printf("max1 = %d, max2=%d",max1,max2);
		}
*/


		max = MAX32(max1, max2);
/*
		if(error_check_flag == 1)
		{
			printf("max = %d",max);
		}
*/

		count1 = AE_MOVAD32_H(countv);
		count2 = AE_MOVAD32_L(countv);
		count = count1 + count2;
/*
		if(error_check_flag == 1)
		{
			printf("count1 = %d, count2 = %d, count = %d\n",count1,count2,count);
		}
*/


		if(odd_num)
		{
			max = MAX32(in[n-1], max);
			if (((int)AE_ABS32S_scalar(in[n-1]))>ref_max_num)
				count++;
		}
/*	
		 for ( ;i<n;i++)
		 {
		    max = MAX32(in[i], max);


		       if (DVTXOP_L_abs(in[i])>ref_max_num)
		        count++;
		 }
*/
		*ret_max_count = count;
		return max;

#endif
}



#endif







#ifdef KHW_RMS_COMPENSATE


#ifndef ZH_DRC_classic_rms_mono32


int int_sqrt_0_50(unsigned int num)		//in: Q31, out:Q15
{
	int i;
	int guess, root;

	root = 0;
	for (i = 15; i >= 0; i--)
	{
		guess = root | (1 << i);
#ifdef VC_PROJ
		if (num >= (guess*guess) ) 
			root = guess;
#else
		if (num >= (unsigned int)(long long)AE_MUL32_HH(guess,guess))
			root = guess;
#endif
	}
	return root;
}


int SoundBooster_DRC_classic_rms_mono32(const int* in, int n)
{
	INT64 EnrgL = 0;
	int k = n;
	int ret_val = 0;

	int tmp;
	unsigned int final;

//#define ZH_DRC_while

#ifndef ZH_DRC_while

#ifndef VC_PROJ
	ae_int64 tmp_val = 0;
#endif
	do {
		int data = *in++;
#ifdef VC_PROJ
		EnrgL += (INT64)data*data;
#else
		AE_MULA32_HH(tmp_val, data, data);
#endif
	} while (--n > 0);

#ifndef VC_PROJ
	EnrgL = (long long)(tmp_val);
#endif

#else   // #ifndef ZH_DRC_while


/*
	ae_int64x2 EnrgLx;
	ae_int32x2 *p_in = (ae_int32x2 *)in;

	do {
		AE_MULA32X2_vector(EnrgLx, *p_in, *p_in);
		p_in++;
		n = n - 2;
	} while (n > 0);
	EnrgL = AE_INT64X2_RADD(EnrgLx);
*/


#endif  // #ifndef ZH_DRC_while

	final = (unsigned int)((EnrgL/k)>>12);
	
	if (final > 0)
	{
	   int bit = 0;
#ifdef VC_PROJ
		bit = 32 - ec_ilog(final);
#else
		if(final<= 2147483647)
			bit = AE_NSAZ32_L(final) + 1;
#endif		
		bit = bit - (bit & 1);
		ret_val = int_sqrt_0_50(final<<bit);
		ret_val = ret_val >> (bit >> 1);
	}


	return ret_val;
}



#else   // ZH_DRC_classic_rms_mono32

#if 1
short int_sqrt_0_50_int(int num)  //in: Q31, out:Q15
{
	int i;
	short guess, root;

	root = 0;
	for (i = 14; i >= 0; i--)
	{
		guess = root | (1 << i);
		if (num >= (guess*guess)) root = guess;
	}
	return root;
}




int SoundBooster_DRC_classic_rms_mono32(const int* in, int n)
{
	INT64 EnrgL = 0;
	// int test = 60000 << 6;   // inÀÇ ÃÖ´ë°ª
	int k = n;
	int ret_val = 0;


	int tmp;
	unsigned int final;

#ifndef VC_PROJ
	ae_int64 tmp_val = 0;
#endif


	do {
		int data = *in++;
#ifdef VC_PROJ
		EnrgL += (INT64)data*data;
#else
		AE_MULA32_HH(tmp_val, data, data);
#endif
	} while (--n > 0);


#ifndef VC_PROJ
	EnrgL = (long long)(tmp_val);
#endif



	// zhuheng : change to use int_sqrt_0_50_int
	{
		int a;
		final = (unsigned int)((EnrgL / k) >> 12);
		if (final > 0)
		{
			int bit = 0;
			bit = 32 - ec_ilog(final);




			if (bit > 2)
			{
				bit = bit - 3;
				bit = bit - (bit & 1);
				ret_val = int_sqrt_0_50_int(final << bit);
				ret_val = ret_val >> (bit >> 1);
			}
			else
			{
				ret_val = int_sqrt_0_50_int(final);
			}

		}

	}

	return ret_val;
}
#else


short int_sqrt_0_50_int(int num)  //in: Q31, out:Q15
{
	int i;
	short guess, root;



	root = 0;
	for (i = 14; i >= 0; i--)
	{
		guess = root | (1 << i);
		if (num >= (guess*guess)) root = guess;
	}
	return root;
}


int SoundBooster_DRC_classic_rms_mono32(const int* in, int n)
{
	INT64 EnrgL = 0;
	// int test = 60000 << 6;   // inÀÇ ÃÖ´ë°ª
	int k = n;
	int ret_val = 0;
	unsigned int final;

	ae_int64x2 EnrgLx;
	ae_int32x2 *p_in = (ae_int32x2 *)in;

	int tmp;
#ifndef VC_PROJ
	ae_int64 tmp_val = 0;
#endif

//	printf("before");

	do {
		AE_MULA32X2_vector(EnrgLx, *p_in, *p_in);
		p_in++;
		n = n - 2;
	} while (n > 0);
	EnrgL = AE_INT64X2_RADD(EnrgLx);
/*
	do {
		int data = *in++;
#ifdef VC_PROJ
		EnrgL += (INT64)data*data;
#else
		AE_MULA32_HH(tmp_val, data, data);
#endif
	} while (--n > 0);

#ifndef VC_PROJ
	EnrgL = (long long)(tmp_val);
#endif
*/
//	printf("after");


	final = (unsigned int)((EnrgL / k) >> 12);


	if (final > 0)
	{
		int bit = 0;
	//	bit = 32 - ec_ilog(final);

		if(final<= 2147483647)
			bit = AE_NSAZ32_L(final) + 1;

		if (bit > 2)
		{
			bit = bit - 3;
			bit = bit - (bit & 1);
			ret_val = int_sqrt_0_50_int(final << bit);
			ret_val = ret_val >> (bit >> 1);
		}
		else
		{
			ret_val = int_sqrt_0_50_int(final);
		}

	}


}
#endif




#endif

#endif




static void SoundBooster_DRC_classic_ApplyGain_mono_direct(int* out,
            int gain_start,int delta_gain,int n)
{

#ifdef DRC_HIFI
	ae_int64 tmp_val;
#endif

    int gain=gain_start;
	int *in = out;
    do{
#ifndef DRC_HIFI
		*out++=(int) (((INT64)(*in++)*gain)>>QGAIN_L);
#else
		tmp_val = AE_MUL32_HH((*in++), gain);
		*out++ = (int)((long long)AE_SRAI64(tmp_val, QGAIN_L));
#endif
        gain+=delta_gain;
    }while(--n>0);
}




static void SoundBooster_DRC_classic_ApplyGain_stereo_direct(int *out1,int *out2,int gain_start,int delta_gain,int n)
{
    int gain=gain_start;

	int *in1 = out1;
	int *in2 = out2;

#ifdef DRC_HIFI
	ae_int64 tmp_val;
#endif
    do{
#ifndef DRC_HIFI
		(*out1++)=(int) (((INT64)(*in1++)*gain)>>QGAIN_L);
		(*out2++)=(int) (((INT64)(*in2++)*gain)>>QGAIN_L);
#else
		tmp_val = AE_MUL32_HH((*in1++), gain);
		*out1++ = (int)((long long)AE_SRAI64(tmp_val, QGAIN_L));
		tmp_val = AE_MUL32_HH((*in2++), gain);
		*out2++ = (int)((long long)AE_SRAI64(tmp_val, QGAIN_L));
#endif

        gain+=delta_gain;
    }while(--n>0);
}



static int piecelinear(int in_dB, int limit_gain)
{

	int x0,x1;

	x0 = (-96<<Q_DRC_DB);
	x1 = limit_gain;

    // 1. Determine dynamic range region
	if(in_dB>(x0) & in_dB>(x1))
	{
		in_dB = x1;
	}

	return in_dB;
}

#define DVTX_MAX_32 (int)0x7fffffffL
#define DVTX_MIN_32 (int)0x80000000L
#define DVTX_MAX_16 (short)0x7fff
#define DVTX_MIN_16 (short)0x8000

#define LOG10QFIX /* fnLog10(2) */ -605866608
#define LOG10FIX  /* fnLog10(1) */ -626068080

#ifdef VC_PROJ

#ifndef HW_FIXED

#define RECIP_LOGF10 (0.434294481903252f)  // 1/logf(10.0f)
static float Log10(float x) { return logf(x)*RECIP_LOGF10; }



#else


#define DVTX_LOG10QFIX 0XDBE33590 //DVTXOP_fnLog10(2)
#define DVTX_LOG10FIX  0xDAAEF590 //DVTXOP_fnLog10(1)
int DVTXOP_Overflow = 0;


int DVTXOP_L_mult(short var1, short var2)
{
	int L_var_out;

	L_var_out = (int)var1 *(int)var2;

	if (L_var_out != (int)0x40000000L)
	{
		L_var_out *= 2;
	}
	else
	{
		DVTXOP_Overflow = 1;
		L_var_out = DVTX_MAX_32;
	}
	return (L_var_out);
}

int DVTXOP_L_add(int L_var1, int L_var2)
{
	int L_var_out;

	L_var_out = L_var1 + L_var2;
	if (((L_var1 ^ L_var2) & DVTX_MIN_32) == 0)
	{
		if ((L_var_out ^ L_var1) & DVTX_MIN_32)
		{
			L_var_out = (L_var1 < 0) ? DVTX_MIN_32 : DVTX_MAX_32;
			DVTXOP_Overflow = 1;
		}
	}

	return (L_var_out);
}


int DVTXOP_L_mac(int L_var3, short var1, short var2)
{
	int L_var_out;
	int L_product;

	L_product = DVTXOP_L_mult(var1, var2);
	L_var_out = DVTXOP_L_add(L_var3, L_product);

	return (L_var_out);
}

short DVTXOP_extract_l(int L_var1)
{
	short var_out;
	var_out = (short)L_var1;
	return (var_out);
}

short DVTXOP_extract_h(int L_var1)
{
	short var_out;
	var_out = (short)(L_var1 >> 16);
	return (var_out);
}

short DVTXOP_saturate(int L_var1)
{
	short var_out;

	if (L_var1 > 0X00007fffL)
	{
		DVTXOP_Overflow = 1;
		var_out = DVTX_MAX_16;
	}
	else if (L_var1 < (int)0xffff8000L)
	{
		DVTXOP_Overflow = 1;
		var_out = DVTX_MIN_16;
	}
	else
	{
		DVTXOP_Overflow = 0;
		var_out = DVTXOP_extract_l(L_var1);
	}

	return (var_out);
}


short DVTXOP_mult_r(short var1, short var2)
{
	short var_out;
	int L_product_arr;
	L_product_arr = (int)var1 *(int)var2;       /* product */
	L_product_arr += (int)0x00004000L;      /* DVTXOP_round */
	L_product_arr &= (int)0xffff8000L;
	L_product_arr >>= 15;       /* shift */
	if (L_product_arr & (int)0x00010000L)   /* sign extend when necessary */
	{
		L_product_arr |= (int)0xffff0000L;
	}
	var_out = DVTXOP_saturate(L_product_arr);
	return (var_out);
}


int DVTXOP_L_sub(int L_var1, int L_var2)
{
	int L_var_out;

	L_var_out = L_var1 - L_var2;

	if (((L_var1 ^ L_var2) & DVTX_MIN_32) != 0)
	{
		if ((L_var_out ^ L_var1) & DVTX_MIN_32)
		{
			L_var_out = (L_var1 < 0L) ? DVTX_MIN_32 : DVTX_MAX_32;
			DVTXOP_Overflow = 1;
		}
	}
	return (L_var_out);
}

short DVTXOP_norm_l(int L_var1)
{
	short var_out;

	if (L_var1 == 0)
	{
		var_out = 0;
	}
	else
	{
		if (L_var1 == (int)0xffffffffL)
		{
			var_out = 31;
		}
		else
		{
			if (L_var1 < 0)
			{
				L_var1 = ~L_var1;
			}
			for (var_out = 0; L_var1 < (int)0x40000000L; var_out++)
			{
				L_var1 <<= 1;
			}
		}
	}

	return (var_out);
}


int DVTXOP_L_deposit_h(short var1)
{
	int L_var_out;

	L_var_out = (int)var1 << 16;

	return (L_var_out);
}


short DVTXOP_shr(short var1, short var2)
{
	short var_out;

	if (var2 < 0)
	{
		var_out = DVTXOP_shl(var1, (short)(-var2));
	}
	else
	{
		if (var2 >= 15)
		{
			var_out = (var1 < 0) ? -1 : 0;
		}
		else
		{
			if (var1 < 0)
			{
				var_out = ~((~var1) >> var2);
			}
			else
			{
				var_out = var1 >> var2;
			}
		}
	}

	return (var_out);
}

short DVTXOP_shl(short var1, short var2)
{
	short var_out;
	int result;

	if (var2 < 0)
	{
		var_out = DVTXOP_shr(var1, (short)(-var2));
	}
	else
	{
		result = (int)var1 *((int)1 << var2);
		if ((var2 > 15 && var1 != 0) || (result != (int)((short)result)))
		{
			DVTXOP_Overflow = 1;
			var_out = (var1 > 0) ? DVTX_MAX_16 : DVTX_MIN_16;
		}
		else
		{
			var_out = DVTXOP_extract_l(result);
		}
	}

	return (var_out);
}


int DVTXOP_L_shr(int L_var1, short var2)
{
	int L_var_out;

	if (var2 < 0)
	{
		L_var_out = DVTXOP_L_shl(L_var1, (short)(-var2));
	}
	else
	{
		if (var2 >= 31)
		{
			L_var_out = (L_var1 < 0L) ? -1 : 0;
		}
		else
		{
			if (L_var1 < 0)
			{
				L_var_out = ~((~L_var1) >> var2);
			}
			else
			{
				L_var_out = L_var1 >> var2;
			}
		}
	}

	return (L_var_out);
}


int DVTXOP_L_shl(int L_var1, short var2)
{
	int L_var_out;

	if (var2 <= 0)
	{
		L_var_out = DVTXOP_L_shr(L_var1, (short)(-var2));
	}
	else
	{
		for (; var2 > 0; var2--)
		{
			if (L_var1 > (int)0X3fffffffL)
			{
				DVTXOP_Overflow = 1;
				L_var_out = DVTX_MAX_32;
				break;
			}
			else
			{
				if (L_var1 < (int)0xc0000000L)
				{
					DVTXOP_Overflow = 1;
					L_var_out = DVTX_MIN_32;
					break;
				}
			}
			L_var1 *= 2;
			L_var_out = L_var1;
		}
	}

	return (L_var_out);
}

int DVTXOP_L_shr_r(int L_var1, short var2)
{
	int L_var_out;

	if (var2 > 31)
	{
		L_var_out = 0;
	}
	else
	{
		L_var_out = DVTXOP_L_shr(L_var1, var2);

		if (var2 > 0)
		{
			if ((L_var1 & ((int)1 << (var2 - 1))) != 0)
			{
				L_var_out++;
			}
		}
	}

	return (L_var_out);
}

short DVTXOP_add(short var1, short var2)
{
	short var_out;
	int L_sum;

	L_sum = (int)var1 + var2;
	var_out = DVTXOP_saturate(L_sum);

	return (var_out);
}

short DVTXOP_sub(short var1, short var2)
{
	short var_out;
	int L_diff;

	L_diff = (int)var1 - var2;
	var_out = DVTXOP_saturate(L_diff);

	return (var_out);
}

short DVTXOP_negate(short var1)
{
	short var_out;
	var_out = (var1 == DVTX_MIN_16) ? DVTX_MAX_16 : -var1;
	return (var_out);
}

int DVTXOP_L_mpy_ls(int L_var2, short var1)
{
	int L_varOut;
	short swtemp;

	//#define MULT16_32_Q15(a,b) ADD32(SHL(MULT16_16((a),SHR((b),16)),1), SHR(MULT16_16SU((a),((b)&0x0000ffff)),15))
	swtemp = DVTXOP_shr(DVTXOP_extract_l(L_var2), 1);
	swtemp = (short)32767 & (short)swtemp;

	L_varOut = DVTXOP_L_mult(var1, swtemp);
	L_varOut = DVTXOP_L_shr(L_varOut, 15);
	L_varOut = DVTXOP_L_mac(L_varOut, var1, DVTXOP_extract_h(L_var2));

	return (L_varOut);
}
#endif // HW_FIXED
#endif   // VC_PROJ





#ifdef HW_FIXED
int DVTXOP_Log2(int L_Input)
{
	static short swC0 = -0x2b2a, swC1 = 0x7fc5, swC2 = -0x54d0;
	short siShiftCnt, swInSqrd, swIn;
	int LwIn;

	/* normalize input and store shifts required */
	/* ----------------------------------------- */
	siShiftCnt = DVTXOP_norm_l(L_Input);
	LwIn = DVTXOP_L_shl(L_Input, siShiftCnt);
	siShiftCnt = DVTXOP_add(siShiftCnt, 1);
	siShiftCnt = DVTXOP_negate(siShiftCnt);

	/* calculate x*x*c0 */
	/* ---------------- */
	swIn = DVTXOP_extract_h(LwIn);
	swInSqrd = DVTXOP_mult_r(swIn, swIn);
	LwIn = DVTXOP_L_mult(swInSqrd, swC0);

	/* DVTXOP_add x*c1 */
	/* --------- */
	LwIn = DVTXOP_L_mac(LwIn, swIn, swC1);

	/* DVTXOP_add c2 */
	/* ------ */
	LwIn = DVTXOP_L_add(LwIn, DVTXOP_L_deposit_h(swC2));

	/* apply *(4/32) */
	/* ------------- */
	LwIn = DVTXOP_L_shr(LwIn, 3);
	LwIn = LwIn & 0x03ffffff;
	siShiftCnt = DVTXOP_shl(siShiftCnt, 10);
	LwIn = DVTXOP_L_add(LwIn, DVTXOP_L_deposit_h(siShiftCnt));

	/* return log2 */
	/* ----------- */
	return (LwIn);
}



int DVTXOP_fnLog10(int L_Input)
{
	static short Scale = 9864;			/* 0.30103 = log10(2) */ //Q15
	int LwIn;

	if (L_Input == 0) return (-2147483647);

	/* 0.30103*log2(x) */
	/* ------------------- */
	LwIn = DVTXOP_Log2(L_Input);
	LwIn = DVTXOP_L_mpy_ls(LwIn, Scale);

	return (LwIn);
}
#if 0
short Fx_10log10(int pwr, short pwr_q)
{
	short pwrdB;

	/* DVTXOP_fnLog10(pwr) - DVTXOP_fnLog10(1) - pwr_q*(DVTXOP_fnLog10(2) - DVTXOP_fnLog10(1)) */
	// pwr: Q26(31-5)

	pwr = DVTXOP_L_sub(DVTXOP_L_sub(DVTXOP_fnLog10(DVTXOP_L_add(pwr, DVTXOP_shl(1, pwr_q))), DVTX_LOG10FIX),
		DVTXOP_L_shl(DVTXOP_L_mpy_ls(DVTXOP_L_sub(DVTX_LOG10QFIX, DVTX_LOG10FIX), DVTXOP_shl(pwr_q, 11)), 4));

	pwr = DVTXOP_L_shr(pwr, 4);                      // Q26 -> Q22
	pwr = DVTXOP_L_shl(DVTXOP_L_mpy_ls(pwr, 0x2800), 5);    // 10 << 10 and then again DVTXOP_shl to obtain Q15
	pwrdB = DVTXOP_extract_l(DVTXOP_L_shr(pwr, 14));         // Q22 -> Q8

	return pwrdB;
}
#endif
int DVTXOP_10log10_l(int pwr, short pwr_q)
{
	int pwrdB;

	int Ltmp1, Ltmp2, Ltmp3;

	Ltmp1 = DVTXOP_L_sub(DVTXOP_fnLog10(pwr), LOG10FIX);	// 8*log10(pwr), Q23
	Ltmp2 = DVTXOP_L_sub(LOG10QFIX, LOG10FIX);		// 8*log10(2), Q23
	Ltmp3 = DVTXOP_L_shl(DVTXOP_L_mpy_ls(Ltmp2, DVTXOP_shl(pwr_q, 10)), 5);	// 8 * pwr_q * log10(2), Q23
	Ltmp2 = DVTXOP_L_sub(Ltmp1, Ltmp3);			// 8*log10(x) = 8*(log10(pwr) - pwr_q*log10(2)), Q23

	pwrdB = DVTXOP_L_add(Ltmp2, DVTXOP_L_shr_r(Ltmp2, 2));	// 10*log10(x): Q23

	return pwrdB;
}


int DVTXOP_fnExp2(int L_Input)
{
	static short pswPCoefE[3] =
	{							/* c2,   c1,    c0 */
		0x15ef, 0x556f, 0x7fbd
	};
	short swTemp1, swTemp2, swTemp3, swTemp4;
	int LwIn;
	swTemp3 = 0x0020;

	/* determine normlization shift count */
	/* ---------------------------------- */
	swTemp1 = DVTXOP_extract_h(L_Input);
	LwIn = DVTXOP_L_mult(swTemp1, swTemp3);
	swTemp2 = DVTXOP_extract_h(LwIn);

	/* determine un-normalized shift count */
	/* ----------------------------------- */
	swTemp3 = -0x0001;
	swTemp4 = DVTXOP_sub(swTemp3, swTemp2);

	/* normalize input */
	/* --------------- */
	LwIn = LwIn & 0xffff;
	LwIn = DVTXOP_L_add(LwIn, DVTXOP_L_deposit_h(swTemp3));
	LwIn = DVTXOP_L_shr(LwIn, 1);
	swTemp1 = DVTXOP_extract_l(LwIn);

	/* calculate x*x*c2 */
	/* ---------------- */
	swTemp2 = DVTXOP_mult_r(swTemp1, swTemp1);
	LwIn = DVTXOP_L_mult(swTemp2, pswPCoefE[0]);

	/* calculate x*x*c2 + x*c1 */
	/* ----------------------- */
	LwIn = DVTXOP_L_mac(LwIn, swTemp1, pswPCoefE[1]);

	/* calculate x*x*c2 + x*c1 + c0 */
	/* --------------------------- */
	LwIn = DVTXOP_L_add(LwIn, DVTXOP_L_deposit_h(pswPCoefE[2]));

	/* un-normalize exponent if its requires it */
	/* ---------------------------------------- */
	if (swTemp4 > 0)
	{
		LwIn = DVTXOP_L_shr(LwIn, swTemp4);
	}

	/* return result */
	/* ------------- */
	return (LwIn);
}


int DVTXOP_fnExp10(int L_Input)
{
	static short InvScale = 27213;		/* (1/log10(2))/4 */
	int LwIn;

	LwIn = DVTXOP_L_mpy_ls(L_Input, InvScale);
	LwIn = DVTXOP_L_shl(LwIn, 2);
	LwIn = DVTXOP_fnExp2(LwIn);

	return (LwIn);
}

#endif



void SoundBooster_DRC_Exe(int* in_out1,int* in_out2, int n)
{
	int gain_var;      
	int tau_release;     
	int tau_attack ;    
	int st;         
	int curr_maxabs;

#ifndef HW_FIXED
	float rmsdB ;
	float outdB;
#else
	int rmsdB_fix;
	int outdB_fix;
#endif

	
	int gain_tgt;
	int tau;
	int g;
	int gain_start;
	int delta_gain;
	int limit_gain = drc_struct.limit_gain;
#ifdef KHW_DRC_DETECTION_RULE
	int max_count;
#endif

#ifdef SB_DRC_ON_REMOVE_BUF_2
	int max_count2;
	int curr_maxabs2;
#endif


#ifdef DRC_HIFI
	ae_int64 tmp_val;
#endif

	gain_var      =drc_struct.gain_var;
	tau_release     =drc_struct.tau_release;
	tau_attack      =drc_struct.tau_attack;
	st              = drc_struct.st;

	// 1. MaxAbs in current frame
#ifndef KHW_DRC_DETECTION_RULE
	curr_maxabs=SoundBooster_DRC_classic_maxabs(in_out,n*DRC_ChNum);
#else

#ifndef SB_DRC_ON_REMOVE_BUF_2
	curr_maxabs=SoundBooster_DRC_classic_maxabs(in_out,n*DRC_ChNum,32760,&max_count);
	max_count = (max_count)*(3-DRC_ChNum);
#else
	if(DRC_ChNum == 1)
	{
		curr_maxabs=SoundBooster_DRC_classic_maxabs(in_out1,n,32760,&max_count);
		max_count = (max_count<<1);
	}
	else
	{
		int i;

		
/*
		if(error_check_flag == 1)
		{
			printf("===========================\n");
			printf("count = %d\n",count);
			printf("n = %d\n",n);
		}

		if(error_check_flag == 1)
		{
			for(i = 0; i<n; i++)
			{
				printf("in_out1[%d] = %d\n",i,in_out1[i]);
			}
			for(i = 0; i<n; i++)
			{
				printf("in_out2[%d] = %d\n",i,in_out2[i]);
			}
		}


		if(error_check_flag == 1)
		{
			printf("inside first SoundBooster_DRC_classic_maxabs\n\n ");
		}
*/

		curr_maxabs=SoundBooster_DRC_classic_maxabs(in_out1,n,32760,&max_count);
/*
		if(error_check_flag == 1)
		{
			printf("inside second SoundBooster_DRC_classic_maxabs\n\n ");
		}
*/

		curr_maxabs2=SoundBooster_DRC_classic_maxabs(in_out2,n,32760,&max_count2);



/*
		if(error_check_flag == 1)
		{
			printf("\ncurr_maxabs = %d\n",curr_maxabs);
			printf("curr_maxabs2 = %d\n",curr_maxabs2);
			printf("max_count = %d\n",max_count);
			printf("max_count2 = %d\n",max_count2);
		}
*/
		max_count = max_count+max_count2;
		curr_maxabs = MAX32(curr_maxabs,curr_maxabs2);

	}

#endif
#endif

	// 2. Gain update
	// input level






#ifndef HW_FIXED
	rmsdB = -96;
	if (curr_maxabs>0)
	{
		rmsdB = 20 * Log10(curr_maxabs/ (float)(1 << (15 + NOISEBITS)));
	}
	if (rmsdB < -96.f) rmsdB = -96.f;

	// compute output level
	outdB = piecelinear(  rmsdB, limit_gain);
	// compute required gain
/*
	{
		float test1 = pow(10.0f, 0);
		int test2= 3;
	}
*/
	gain_tgt = (int)(pow(10.0f, (outdB - rmsdB) / 20.0f)*(1 << QGAIN_L));


/*
	if(outdB != rmsdB)
	{
		printf("asdf");
	}
*/
#else
	rmsdB_fix = -96<<Q_DRC_DB;

#ifndef KHW_DRC_DETECTION_RULE
	if (curr_maxabs>0)    // 20log10ÀÇ Á¤È®µµ ³Ê¹« ¶³¾îÁü.
#else
	if (curr_maxabs>0 && (max_count>=7))    // 20log10ÀÇ Á¤È®µµ ³Ê¹« ¶³¾îÁü.
#endif
	{
	//	rmsdB = 2*(Fx_10log10(curr_maxabs<<15,0) - ((10*Log10(32768<<15))*(1<<8)));
	//	rmsdB = rmsdB/256;
	//	rmsdB = 2*(DVTXOP_10log10_l(curr_maxabs<<15,0) - ((10*Log10(32768<<15))*(1<<23)));   // 757566789
		rmsdB_fix = (DVTXOP_10log10_l(curr_maxabs<<15,0) - 757566789)<<1;   // 757566789
	}
	if (rmsdB_fix < -96<<Q_DRC_DB) 
		rmsdB_fix = (-96<<Q_DRC_DB);

	// compute output level
	outdB_fix = piecelinear(  rmsdB_fix, limit_gain);


/*
	if(outdB_fix != rmsdB_fix)
	{
		printf("sdfsdf");
	}
*/
	// compute required gain
/*
	{
		float test_float;
		int input = -1;
		int test1,test2,test3;
		test1 = DVTXOP_fnExp10(input);
		test1 = test1>>9;
		test_float = pow(10.0f,(float)(input)/(1<<26));
		test2 = (int)(test_float * (1 << QGAIN_L));
		test3 = test1;
	}
*/	
	gain_tgt = 1<<22;
	outdB_fix = ((outdB_fix - rmsdB_fix)<<3)/20;
	if(outdB_fix<0)
	{
		gain_tgt = DVTXOP_fnExp10(outdB_fix);   //
		gain_tgt = gain_tgt>>9;
	//	gain_tgt = (int)(pow(10.0f, (((float)(outdB_fix - rmsdB_fix))/(1<<Q_DRC_DB)) / 20.0f)*(1 << QGAIN_L));
	}

#endif

	


	// smoothing gain
	if (gain_tgt>gain_var)    tau = tau_release;
	else                        tau = tau_attack;

//////////////////////////////////////////////////////////////////////////////////
#ifndef DRC_HIFI
	gain_var = (int)(((INT64)gain_var*tau + (INT64)gain_tgt*((1 << QTAU) - tau)) >> QTAU);
#else
	tmp_val = AE_MUL32_HH(gain_var, tau);
	tmp_val = tmp_val +  AE_MUL32_HH(gain_tgt, ((1 << QTAU) - tau));
	gain_var = (int)((long long)AE_SRAI64(tmp_val, QTAU));
#endif

/*
#ifndef DRC_HIFI
	*out++=(int) (((INT64)(*in++)*gain)>>QGAIN_L);
#else
	tmp_val = AE_MUL32_HH((*in++), gain);
	*out++ = (int)((long long)AE_SRAI64(tmp_val, QGAIN_L));
#endif
*/
//////////////////////////////////////////////////////////////////////////////////

	drc_struct.gain_var = gain_var;    // store drc_struct data

	// postprocess gain_var
	// down - immediately, up - apply previous gain
	g = gain_var;
	if (g > st) gain_var = st;
	st = g;

	gain_start = drc_struct.gain_start;

	// 3. Apply Gain (to delayed version), put new data to buffer
	delta_gain=(gain_var-gain_start)/n;



	if(delta_gain != 0 || gain_start != (1<<QGAIN_L))
	{
		if(DRC_ChNum == 2)
		{
			SoundBooster_DRC_classic_ApplyGain_stereo_direct(in_out1,in_out2, gain_start,delta_gain,n);
		}
		else
		{
			SoundBooster_DRC_classic_ApplyGain_mono_direct(in_out1, gain_start,delta_gain,n);
		}
	}


	// store drc_struct data
	drc_struct.gain_start = gain_var;
	drc_struct.st = st;
}




#define ATTACK_TIME_MAX        500000
#define RELEASE_TIME_MAX      2000000
#define LOOK_FORWARD_TIME_MAX   50000


void SoundBooster_DRC_SetPar(
						int Mode,
						int limit_gain,
						int Attack_Time,
						int Release_Time,
						int Look_Forward_Time)

{

	switch (Mode)	{
	case 0: default: DRC_ModeFlag = DRC_OFF; break;
	case 1: DRC_ModeFlag = DRC_PEAK; break;
	}

    if (Attack_Time<0)  Attack_Time=0;
    if (Attack_Time>ATTACK_TIME_MAX)  Attack_Time=ATTACK_TIME_MAX;

#if 0    //  attck time °è»ê
	drc_struct.tau_attack = (int)(2*(expf(-2.2f*FRAME/Soundbooster_DRC_Fs/(Attack_Time *1e-6f))*(1<<QTAU)));
	drc_struct.tau_attack = (drc_struct.tau_attack>>1);
#else
	drc_struct.tau_attack = 0;
#endif

    if (Release_Time<0)  Release_Time=0;
    if (Release_Time>RELEASE_TIME_MAX)  Release_Time=RELEASE_TIME_MAX;

#if 0    //  release time °è»ê
	  drc_struct.tau_release= (int)(2*(expf(-2.2f*FRAME/Soundbooster_DRC_Fs/(Release_Time*1e-6f))*(1<<QTAU)));
	  drc_struct.tau_release =  (drc_struct.tau_release>>1);
#else
	   drc_struct.tau_release = 3765845;    // Frame 108, release_Time 50000 Àû¿ë, ÃßÈÄ °è»ê½Ã ´Ù½Ã °ª ¼³Á¤.
#endif
    // gain curve copy
 //   memcpy(&(drc_struct.gain_curve_table),gaincurve,sizeof(SoundBooster_DRC_classic_GainCurve_T));

	drc_struct.limit_gain = limit_gain;


    // gain smoothing initial state (expected signal level ~-10dB at the start of track)
	drc_struct.gain_var = 1 << QGAIN_L;

    // look forward buffer
	Look_Forward_Time=0;


}

#define MIN_FS 8000
#define MAX_FS 192000

void SoundBooster_DRC_Init(int sr, int chn)
{

	if (sr < MIN_FS) sr = MIN_FS;
	if (sr > MAX_FS) sr = MAX_FS;

	Soundbooster_DRC_Fs = sr;

	if (chn != 2) chn = 1;
	DRC_ChNum = chn;

    drc_struct.st = (1 << (7 + QGAIN_L));               // fake max imaginable gain
    drc_struct.gain_start = (1 << (QGAIN_L));
	

}

static const char Version_string[] = "SoundBooster_DRC_classic for SBM ver 1.0 Release: 2019.12.05";

const char* SoundBooster_DRC_Get_Version(void)
{
    return Version_string;
}


