/************************************
		SV_NS.c
/************************************/

#include "SV_NS.h"
#include <math.h>

#define Nfrmsnr 4

#define MIN_EE 1

#define SIZE_xioptTABLE		960

static const int Table_xiopt_FixXi1[SIZE_xioptTABLE] = {
#include "xiopt_table_FixXi1.dat"
};
static const int Table_xiopt_FixXi2[SIZE_xioptTABLE] = {
#include "xiopt_table_FixXi2.dat"
};
static const int Table_xiopt_FixXi5[SIZE_xioptTABLE] = {
#include "xiopt_table_FixXi5.dat"
};

static int EE[NUM_CHAN];
static int NN[NUM_CHAN];
static int Npsd[NUM_CHAN];            int Q_Npsd;
static int Npsd_1st[NUM_CHAN];        int Q_Npsd_1st;
static int Npsd_estimated[NUM_CHAN]; int Q_Npsd_estimated;
static int spp[NUM_CHAN];
static int spp_1st_smooth[NUM_CHAN]; //Q31
static int snr_mtx[Nfrmsnr][NUM_CHAN]; //Q22
static int gain_Npsd;
static int G_NS[NUM_CHAN];
static int postSNRprev[NUM_CHAN];
static int prioSNR_avg[NUM_CHAN];/*Q22*/
static int noise_floor_offset;
static int G_NS_final[NUM_CHAN];

static const char FixXi[NUM_CHAN][3] = {
		{ 2, 5, 5 },    // 31 	~31
		{ 2, 5, 5 },    // 63 	~63
		{ 2, 5, 5 },    // 94 	~94
		{ 2, 5, 5 },    // 125 	~156
		{ 2, 5, 5 },    // 188 	~219
		{ 2, 5, 5 },    // 250 	~313
		{ 2, 5, 5 },    // 344 	~406
		{ 2, 5, 5 },    // 438 	~500
		{ 2, 5, 5 },    // 531 	~625
		{ 2, 5, 5 },    // 656 	~750
		{ 2, 5, 5 },    // 781 	~875
		{ 2, 5, 5 },    // 906 	~1000
		{ 2, 5, 5 },    // 1031 ~1156
		{ 2, 5, 5 },    // 1188 ~1313
		{ 2, 5, 5 },    // 1344 ~1469
		{ 2, 5, 5 },    // 1500 ~1625
		{ 2, 5, 5 },    // 1656 ~1781
		{ 2, 5, 5 },    // 1813 ~1969
		{ 2, 5, 5 },    // 2000 ~2156
		{ 1, 1, 1 },    // 2188 ~2344
		{ 1, 1, 1 },    // 2375 ~2531
		{ 1, 1, 1 },    // 2563 ~2719
		{ 1, 1, 1 },    // 2750 ~2906
		{ 1, 1, 1 },    // 2938 ~3094
		{ 1, 1, 1 },    // 3125 ~3281
		{ 1, 1, 1 },    // 3313 ~3500
		{ 1, 1, 1 },    // 3531 ~3719
		{ 1, 1, 1 },    // 3750 ~3938
		{ 1, 1, 1 },    // 3969 ~4188
		{ 1, 1, 1 },    // 4219 ~4438
		{ 1, 1, 1 },    // 4469 ~4688
		{ 1, 1, 1 },    // 4719 ~5000
		{ 1, 1, 1 },    // 5031 ~5313
		{ 2, 5, 5 },    // 5344 ~5688
		{ 2, 5, 5 },    // 5719 ~6063
		{ 2, 5, 5 },    // 6094 ~6438
		{ 2, 5, 5 },    // 6469 ~6813
		{ 2, 5, 5 },    // 6844 ~7188
		{ 2, 5, 5 },    // 7219 ~7563
		{ 2, 5, 5 },    // 7594 ~7969
};

// Processing
static void fn_Get_xiopt_fromTable(int *xiopt, int idxFixXi, int invSNR, int n)
{
	int i;

	invSNR = MIN(invSNR, SIZE_xioptTABLE - 1);

	for (i = 0; i < n; i++)
	{
		if (FixXi[i][idxFixXi] == 1)
			xiopt[i] = Table_xiopt_FixXi1[invSNR];
		else if (FixXi[i][idxFixXi] == 2)
			xiopt[i] = Table_xiopt_FixXi2[invSNR];
		else // (FixXi[i][idxFixXi] == 5)
			xiopt[i] = Table_xiopt_FixXi5[invSNR];
	}

	return;
}

static int fn_2nd_Npsd_estimation(int *Npsd, int Q_Npsd, int *EE, int Q_EE, int *ph1_GbyL, int *snr, int frmVAD, int noiseLevel)
{
	int i, j;
	double invSNR_double = 0;
	int mean_snr = 0;
	int invSNR = 0;
	int p1_local[NUM_CHAN] = { 0, };
	int p1_global[NUM_CHAN] = { 0, };
	int xiopt[NUM_CHAN] = { 0, };
	int snr_smooth[NUM_CHAN] = { 0, }; //Q22
	int snr_global[NUM_CHAN] = { 0, };
	int MaxSPP = 0;
	int BetaMin = 0;
	int forgetOld = 0;
	int Q_Npsd_new = 0;
	INT64 sum64 = 0;

	sum64 = 0;
	for (i = 0; i < NUM_CHAN; i++)
		sum64 += snr[i];
	mean_snr = (int)((sum64 * 53687091) >> 31);		//mean_snr = sum64 / NUM_CHAN;

	invSNR_double = (15 - MIN(MAX(GetdB((float)mean_snr / 4194304.), 0), 15));// / 3; //[0~5] -> [0~15]
	invSNR = (int)(invSNR_double * (1 << 6)); // Q6

	// ph1 (local)
	fn_Get_xiopt_fromTable(xiopt, 1, invSNR, NUM_CHAN);
	fn_Get_posterioriSPP(p1_local, snr, xiopt, 1, NUM_CHAN);

	// ph1 (global)
	//memcpy(&snr_mtx[1], snr_mtx, sizeof(int)*(3 * NUM_CHAN));
	for (i = 0; i < NUM_CHAN; i++)
	{
		snr_mtx[3][i] = snr_mtx[2][i];
		snr_mtx[2][i] = snr_mtx[1][i];
		snr_mtx[1][i] = snr_mtx[0][i];
	}
	memcpy(snr_mtx, snr, sizeof(int)*NUM_CHAN);
	for (i = 0; i < NUM_CHAN; i++)
	{
		sum64 = 0;
		for (j = 0; j < Nfrmsnr; j++)
			sum64 += snr_mtx[j][i];
		snr_smooth[i] = (int)(sum64 >> 2);// / (double)Nfrmsnr;
	}
	fn_SpectralSmoothing(snr_global, snr_smooth, 1, NUM_CHAN);

	fn_Get_xiopt_fromTable(xiopt, 2, invSNR, NUM_CHAN);
	fn_Get_posterioriSPP(p1_global, snr_global, xiopt, 5, NUM_CHAN);

	if (frmVAD == 0)
	{
		if (noiseLevel > 2)
			MaxSPP = 393216000; // 6000 / 32768;
		else if (noiseLevel > 1)
			MaxSPP = 917504000; // 14000 / 32768;
		else if (noiseLevel > 0)
			MaxSPP = 1703936000; // 26000 / 32768;
		else
			MaxSPP = 2147483647;

		for (i = 0; i < NUM_CHAN; i++)
		{
			p1_global[i] = MIN(p1_global[i], MaxSPP);
			p1_local[i] = MIN(p1_local[i], MaxSPP);
		}
	}

	for (i = 0; i < NUM_CHAN; i++)
		ph1_GbyL[i] = (int)(((INT64)p1_global[i] * p1_local[i]) >> 31);

	// Npsd 2nd estimation
	if (frmVAD == 0)
	{
		BetaMin = 1932735283; //0.9
		for (i = 0; i < NUM_CHAN; i++)
		{
			forgetOld = BetaMin + (int)(((INT64)(2147483647 - BetaMin) * ph1_GbyL[i]) >> 31);
			Q_Npsd_new = fn_TimeSmoothing(&Npsd[i], &Npsd[i], Q_Npsd, &EE[i], Q_EE, forgetOld, 1);
		}
	}
	else
	{
		Q_Npsd_new = Q_Npsd;
	}

	return Q_Npsd_new;
}

static int fn_1st_Npsd_estimation(int *Npsd, int Q_Npsd, int *spp_smooth, int *EE, int Q_EE, int *Npsd_BM, int Q_Npsd_BM, int *snr, int frmVAD, int noiseLevel, int band_low)
{
	/*
	update : Npsd (Q0), spp_smooth(Q31)
	input  : snr (Q22), EE64(Q0), Npsd_BM(Q0)
	*/
	int i;
	int xiopt[NUM_CHAN] = { 0, }; //Q25
	int spp[NUM_CHAN] = { 0, };
	int Npsd_BF[NUM_CHAN] = { 0, };
	int forgetOld = 0;
	int Q_Npsd_estimated_tmp = 0, Q_Npsd_tmp = 0;

	// SNR to PH1 (lookuptable)
	for (i = 0; i < NUM_CHAN; i++)
	{
		//xiopt[i] = pow(10, ((double)FixXi[i][0] / 10));
		if (FixXi[i][0] == 1)
			xiopt[i] = 42242527; //(10^(1/10))*(2^25)
		else if (FixXi[i][0] == 2)
			xiopt[i] = 53180190; //(10^(2/10))*(2^25)
		else
			xiopt[i] = 0;
	}

	fn_Get_posterioriSPP(spp, snr, xiopt, 1, NUM_CHAN);

	for (i = 0; i < NUM_CHAN; i++)		spp[i] = ((spp[i] >> 2) + 1610612736);

	fn_Prevent_stagnation(spp, spp_smooth, 1073741824, NUM_CHAN);

	// recursiveAVG
	if (frmVAD == 1)
		return Q_Npsd;

	for (i = 0; i < HFFTLEN; i++)
		Npsd_BM[i] = (int)(((INT64)Npsd_BM[i] * 1245184000) >> 31);
	fn_bin2band(Npsd_BF, Npsd_BM, tx_ch_tbl, 1, NUM_CHAN);

	for (i = 0; i < band_low; i++)
	{
		forgetOld = MAX(spp[i], 1717986918/*0.8*/);
		Q_Npsd_estimated_tmp = fn_TimeSmoothing(&Npsd_estimated[i], &Npsd_estimated[i], Q_Npsd_estimated, &EE[i], Q_EE, forgetOld, 1);

		forgetOld = 1717986918/*0.8*/;
		Q_Npsd_tmp = fn_TimeSmoothing(&Npsd[i], &Npsd[i], Q_Npsd, &Npsd_estimated[i], Q_Npsd_estimated_tmp, forgetOld, 1);

		Npsd[i] = MAX(Npsd[i], 1);
	}
	for (; i < NUM_CHAN; i++)
	{
		forgetOld = MAX(spp[i], 2040109465/*0.8*/);
		Q_Npsd_estimated_tmp = fn_TimeSmoothing(&Npsd_estimated[i], &Npsd_estimated[i], Q_Npsd_estimated, &EE[i], Q_EE, forgetOld, 1);

		forgetOld = 2040109465/*0.8*/;
		Q_Npsd_tmp = fn_TimeSmoothing(&Npsd[i], &Npsd[i], Q_Npsd, &Npsd_estimated[i], Q_Npsd_estimated_tmp, forgetOld, 1);

		Npsd[i] = MAX(Npsd[i], 1);
	}
	Q_Npsd_estimated = Q_Npsd_estimated_tmp;
	Q_Npsd = Q_Npsd_tmp;

	forgetOld = 1048576000; // (float)16000 / 32768;
	Q_Npsd = fn_TimeSmoothing(Npsd, Npsd, Q_Npsd, Npsd_BF, Q_Npsd_BM, forgetOld, NUM_CHAN);

	if (noiseLevel > 2)
		forgetOld = 644245094; // 0.3;
	else if (noiseLevel > 1)
		forgetOld = 1073741824; // 0.5;
	else if (noiseLevel > 0)
		forgetOld = 1717986918; // 0.8;
	else
		forgetOld = 2040109465; // 0.95;
	Q_Npsd = fn_TimeSmoothing(Npsd, Npsd, Q_Npsd, EE, Q_EE, forgetOld, NUM_CHAN);

	return Q_Npsd;
}

static int fn_NoiseSpectrumEstimation(int *Npsd, int Q_Npsd, int *EE, int Q_EE, int *NN, int Q_NN, int *ph1_GbyL, int *REchoPSDch, SV_NS_T* stNS)
{
	int i;
	int postSNR[NUM_CHAN] = { 0, }; //Q22
	int Npsd_1st_REchoPSD[NUM_CHAN] = { 0, };

	int NpsdBias = stNS->NpsdBias;
	int frmVAD = stNS->frmVAD;
	int noiseLevel = stNS->noiseLevel;
	int band_low = stNS->band_low;
	INT64 *NpsdBM64 = stNS->NpsdBM;
	int NpsdBM[HFFTLEN] = { 0, };

	int Q_NpsdBM = 0;

	if (stNS->FirstFrame == 1)
	{
		memcpy(Npsd_1st, NN, sizeof(int)*NUM_CHAN);			Q_Npsd_1st = Q_NN;
		memcpy(Npsd_estimated, NN, sizeof(int)*NUM_CHAN);	Q_Npsd_estimated = Q_NN;
		memcpy(Npsd, NN, sizeof(int)*NUM_CHAN);				Q_Npsd = Q_NN;
	}

	Q_NpsdBM = GetBlockNorm64(NpsdBM64, HEADROOM_NS, HFFTLEN); //©ø¨£A©¬¢¯¢® ¨ú©ª¨úU ¡ÆI (NpsdBM64 -> 32bit¡¤I). 
	Conver64toInt(NpsdBM, NpsdBM64, Q_NpsdBM, MIN_EE, HFFTLEN);

	// initial SNR estimation 
	for (i = 0; i < NUM_CHAN; i++)
		Npsd_1st_REchoPSD[i] = Npsd_1st[i] + REchoPSDch[i];
	fn_Get_postSNR(postSNR, EE, Q_EE, Npsd_1st_REchoPSD, Q_Npsd_1st, NpsdBias, (int)8192, (int)2147483647);

	// Npsd_1st64
	Q_Npsd_1st = fn_1st_Npsd_estimation(Npsd_1st, Q_Npsd_1st, spp_1st_smooth, EE, Q_EE, NpsdBM, Q_NpsdBM, postSNR, frmVAD, noiseLevel, band_low);

	// second Npsd mmse
	Q_Npsd = fn_2nd_Npsd_estimation(Npsd, Q_Npsd, EE, Q_EE, ph1_GbyL, postSNR, frmVAD, noiseLevel);

	return Q_Npsd;
}

static void fn_SPP(int *spp, int *EE, int Q_EE, int *Npsd, int Q_Npsd, int NpsdBias, int alpha_time, int alpha_freq, int n)
{
	int i;
	int postSNR[NUM_CHAN] = { 0, };
	int xiopt[NUM_CHAN] = { 0, };
	int ph1[NUM_CHAN] = { 0, };

	// posteriori SNR
	fn_Get_postSNR(postSNR, EE, Q_EE, Npsd, Q_Npsd, NpsdBias, 8192/*1 / 512*/, 188743680/*45*/);

	// conditional speech presence probability
	for (i = 0; i < n; i++)
	{
		xiopt[i] = 33554432; // (10^(0/10))*(2^25)
	}
	fn_Get_posterioriSPP(ph1, postSNR, xiopt, 1, n);

	// time/frequency smoothing
	fn_SpectralSmoothing(ph1, ph1, alpha_freq, n);

	fn_TimeSmoothing(spp, spp, 31, ph1, 31, alpha_time, n);

	return;
}

static int fn_Npsd_gain(int gain_Npsd, int FLAG_tx_noisefree)
{
	int beta;

	if (FLAG_tx_noisefree == 1)
	{
		beta = 1932735283; // 0.9;
		gain_Npsd = (int)(((INT64)beta * gain_Npsd + (INT64)(2147483647 - beta) * 1932735283) >> 31);
	}
	else
	{
		beta = 1825361100; // 0.85;
		gain_Npsd = (int)(((INT64)beta * gain_Npsd + (INT64)(2147483647 - beta) * 2147483647) >> 31);
	}

	return gain_Npsd;
}

static void fn_decisiondirected_prioriSNR(int *prioSNR/*Q22*/, int *EE, int Q_EE, int *Npsd, int Q_Npsd, int gain_Npsd/*Q31*/, int *Gmmse/*Q31*/)
{
	int i;
	int postSNR[NUM_CHAN]/*Q22*/ = { 0, };
	int instSNR[NUM_CHAN]/*Q22*/ = { 0, };
	int Npsd_gain[NUM_CHAN] = { 0, };
	int Gmmse_postNSR[NUM_CHAN] = { 0, };

	// postSNR_double estimation
	for (i = 0; i < NUM_CHAN; i++)
		Npsd_gain[i] = (int)(((INT64)Npsd[i] * gain_Npsd) >> 31);
	fn_Get_postSNR(postSNR, EE, Q_EE, Npsd_gain, Q_Npsd, 1, 32768/*1 / 128*/, 536870912/*128*/);

	// DD SNRprio estimation
	for (i = 0; i < NUM_CHAN; i++)
		instSNR[i] = MAX(postSNR[i] - 4194304/*1<<22*/, 0);

	for (i = 0; i < NUM_CHAN; i++)
	{
		Gmmse_postNSR[i]/*Q31*/ = (int)(((INT64)Gmmse[i] * Gmmse[i]) >> 31);
		Gmmse_postNSR[i]/*Q22*/ = (int)(((INT64)Gmmse_postNSR[i] * postSNRprev[i]) >> 31);
	}

	fn_TimeSmoothing(prioSNR/*Q22*/, Gmmse_postNSR, 22, instSNR, 22, 1932735283/*0.9<<22*/, NUM_CHAN);

	for (i = 0; i < NUM_CHAN; i++)
		prioSNR[i]/*Q22*/ = MIN(MAX(prioSNR[i], 32768/*1 / 128*/), 536870912/*128*/);

	memcpy(postSNRprev/*Q22*/, postSNR, sizeof(int)*NUM_CHAN);

	return;
}

static void fn_prioriSNR_smoothing(int *prioSNR_sm, int *prioSNR, int n)
{
	fn_TimeSmoothing(prioSNR_avg, prioSNR_avg, 0, prioSNR, 0, 1825361101/*0.85*/, 28);

	fn_TimeSmoothing(&prioSNR_avg[28], &prioSNR_avg[28], 0, &prioSNR[28], 0, 1073741824/*0.5*/, n - 28);

	fn_SpectralSmoothing(prioSNR_sm, prioSNR_avg, 4, n);

	return;
}

static void fn_noise_floor(int *band_noise_floor/*Q31*/, int *snr/*Q22*/, int frmVAD, int flag_Tx_silence, int n)
{
	int i;
	int beta;
	int x1, x2, y1, y2;
	int NoiseFloorOffset_0_250Hz_low;
	int NoiseFloorOffset_0_250Hz_high;
	int NoiseFloorOffset_250_4000Hz_low;
	int NoiseFloorOffset_250_4000Hz_high;
	int NoiseFloorOffset_4000_8000Hz_low;
	int NoiseFloorOffset_4000_8000Hz_high;

	if (flag_Tx_silence == 1)
	{
		beta = 1932735283; // 0.9;
		noise_floor_offset = (int)((((INT64)beta * noise_floor_offset) >> 31) + 21474836/*(1 - beta)*0.1*/); //Q31
	}
	else
	{
		beta = 1932735283; // 0.9;
		noise_floor_offset = (int)((((INT64)beta * noise_floor_offset) >> 31)/* + (1 - beta)*0.0*/);
	}

	x1 = 2646427/*Q22*/;// pow((double)10, (double)(-2 / 10));
	x2 = 41943040/*Q22*/; // pow((double)10, (double)(10 / 10));

	if (flag_Tx_silence == 1)
	{
		NoiseFloorOffset_0_250Hz_low = 257687552;			// (double)3932 / 32768;
		NoiseFloorOffset_0_250Hz_high = 300613632;			// (double)4587 / 32768;
		NoiseFloorOffset_250_4000Hz_low = 257687552;		// (double)3932 / 32768;
		NoiseFloorOffset_250_4000Hz_high = 300613632;		// (double)4587 / 32768;
		NoiseFloorOffset_4000_8000Hz_low = 257687552;		// (double)3932 / 32768;
		NoiseFloorOffset_4000_8000Hz_high = 300613632;		// (double)4587 / 32768;
	}
	else
	{
		if (frmVAD == 0)
		{
			NoiseFloorOffset_0_250Hz_low = 42926080;		// (double)655 / 32768;
			NoiseFloorOffset_0_250Hz_high = 85852160;		// (double)1310 / 32768;
			NoiseFloorOffset_250_4000Hz_low = 42926080;		// (double)655 / 32768;
			NoiseFloorOffset_250_4000Hz_high = 85852160;	// (double)1310 / 32768;
			NoiseFloorOffset_4000_8000Hz_low = 42926080;	// (double)655 / 32768;
			NoiseFloorOffset_4000_8000Hz_high = 85852160;	// (double)1310 / 32768;
		}
		else
		{
			NoiseFloorOffset_0_250Hz_low = 85852160;		// (double)1310 / 32768;
			NoiseFloorOffset_0_250Hz_high = 128843776;		// (double)1966 / 32768;
			NoiseFloorOffset_250_4000Hz_low = 85852160;		// (double)1310 / 32768;
			NoiseFloorOffset_250_4000Hz_high = 128843776;	// (double)1966 / 32768;
			NoiseFloorOffset_4000_8000Hz_low = 85852160;	// (double)1310 / 32768;
			NoiseFloorOffset_4000_8000Hz_high = 128843776;	// (double)1966 / 32768;
		}
	}

	y1 = noise_floor_offset + NoiseFloorOffset_0_250Hz_low;
	y2 = noise_floor_offset + NoiseFloorOffset_0_250Hz_high;
	for (i = 0; i < 5; i++)
	{
		band_noise_floor[i] = fx_SV_32bit_Divide((y2 - y1) >> 9, x2 - x1, 1, 0);
		band_noise_floor[i] = (int)(((INT64)band_noise_floor[i] * (snr[i] - x1)) >> 22) + y1;
		band_noise_floor[i] = MIN(MAX(band_noise_floor[i], y1), y2);
	}

	y1 = noise_floor_offset + NoiseFloorOffset_250_4000Hz_low;
	y2 = noise_floor_offset + NoiseFloorOffset_250_4000Hz_high;
	for (; i < 28; i++)
	{
		band_noise_floor[i] = fx_SV_32bit_Divide((y2 - y1) >> 9, x2 - x1, 1, 0);
		band_noise_floor[i] = (int)(((INT64)band_noise_floor[i] * (snr[i] - x1)) >> 22) + y1;
		band_noise_floor[i] = MIN(MAX(band_noise_floor[i], y1), y2);
	}

	y1 = noise_floor_offset + NoiseFloorOffset_4000_8000Hz_low;
	y2 = noise_floor_offset + NoiseFloorOffset_4000_8000Hz_high;
	for (; i < NUM_CHAN; i++)
	{
		band_noise_floor[i] = fx_SV_32bit_Divide((y2 - y1) >> 9, x2 - x1, 1, 0);
		band_noise_floor[i] = (int)(((INT64)band_noise_floor[i] * (snr[i] - x1)) >> 22) + y1;
		band_noise_floor[i] = MIN(MAX(band_noise_floor[i], y1), y2);
	}

	fn_SpectralSmoothing(band_noise_floor, band_noise_floor, 4, NUM_CHAN);

	return;
}

static void fn_NS_gain_smoothing(int *G_final, int *Gmmse, int *ph1_GbyL, int *spp, int *band_noise_floor, int n)
{
	int i;
	int spp_ns[NUM_CHAN] = { 0, };
	int G_NS_init[NUM_CHAN] = { 0, };
	int G_NS[NUM_CHAN] = { 0, };

	// spp for NS becomes average between ph1_GbyL(hard spp) and spp(soft spp)
	fn_TimeSmoothing(spp_ns, spp, 31, ph1_GbyL, 31, 429496730/*0.2*/, 28);
	fn_TimeSmoothing(&spp_ns[28], &spp[28], 31, &ph1_GbyL[28], 31, 214748365/*0.1*/, NUM_CHAN - 28);

	// gain calculation : G=G*spp + noisefloor*(1-spp)
	for (i = 0; i < NUM_CHAN; i++)
		G_NS_init[i] = (int)(((INT64)spp_ns[i] * Gmmse[i] + (INT64)(2147483647 - spp_ns[i]) * band_noise_floor[i]) >> 31);

	fn_spectralavg_Soft3(G_NS, G_NS_init, NUM_CHAN);

	for (i = 0; i < 8; i++)
		G_final[i] = (int)(((INT64)1073741824 * G_final[i] + (INT64)1073741824 * G_NS[i]) >> 31);
	for (; i < NUM_CHAN; i++)
		G_final[i] = (int)(((INT64)214748365 * G_final[i] + (INT64)1932735283 * G_NS[i]) >> 31);

	return;
}

#ifdef USEOPT
extern void SV_NS_GetPSD_INT64(INT64* psd, ComplexInt* rbuf, int n);
#else
static void SV_NS_GetPSD_INT64(INT64* psd, ComplexInt* rbuf, int n)
{
	do
	{
		*psd++ = (INT64)(rbuf->re) * rbuf->re + (INT64)(rbuf->im) * rbuf->im;
		rbuf++;
	} while (--n > 0);

	return;
}
#endif

#ifdef USEOPT
extern void SV_NS_fn_NSoutBin(ComplexInt *pXtx, int *G_NS_final, int *G_H1_res, short (*ptx_ch_tbl)[3], int num_chan);
#else
//static void SV_NS_fn_NSoutBin(ComplexInt *Xtx, int *G_NS_final, int *G_H1_res, short (*ptx_ch_tbl)[3], int num_chan)
//{
//    int i, j;
//    int G_bin[HFFTLEN] = { 0, };
//
//    for (i = 0; i < num_chan; i++)
//    {
//        for (j = ptx_ch_tbl[i][0]; j < ptx_ch_tbl[i][1]; j++)
//
//            G_bin[j] = (int)(((INT64)G_NS_final[i] * G_H1_res[i]) >> 31);
//    }
//
//    G_bin[0] = 2147483647;
//    G_bin[HFFTLEN - 1] = 2147483647;
//
//    for (i = 0; i < HFFTLEN; i++)
//    {
//        Xtx[i].re = (int)(((INT64)Xtx[i].re * G_bin[i]) >> 31);
//        Xtx[i].im = (int)(((INT64)Xtx[i].im * G_bin[i]) >> 31);
//    }
//
//    return;
//}

static void SV_NS_fn_NSoutBin(ComplexInt *pXtx, int *G_NS_final, int *G_H1_res, short (*ptx_ch_tbl)[3], int num_chan)
	{
    pXtx++;
    for (int ch=0;ch<num_chan;ch++)
	{
        int G_bin=(int)(((INT64)G_NS_final[ch] * G_H1_res[ch]) >> 31);
        int K = ptx_ch_tbl[ch][1] - ptx_ch_tbl[ch][0];
        do{
            pXtx->re = (int)(((INT64)pXtx->re * G_bin) >> 31);
            pXtx->im = (int)(((INT64)pXtx->im * G_bin) >> 31);
            pXtx++;
        }while (--K>0);
	}
	return;
}

#endif

void SV_NS_Exe(ComplexInt* Xtx, int nsp, SV_NS_T* stNS)
{
	int Q_EE, EE_bin[HFFTLEN] = { 1, }, Q_NN, NN_bin[HFFTLEN] = { 1, };
	INT64 EE_bin64[HFFTLEN] = { 1, };
	INT64 *NN_bin64 = stNS->Npsdgsc;
	int G_H1_res[HFFTLEN] = { 1, }; double *G_H1_res_double = stNS->G_H1_res;
	int *REchoPSDch = stNS->REchoPSDch;
	int ph1_GbyL[NUM_CHAN] = { 1, };
	int prioSNR[NUM_CHAN] = { 1, };/*Q22*/
	int prioSNR_sm[NUM_CHAN] = { 1, };/*Q22*/
	int band_noise_floor[NUM_CHAN] = { 1, };

	int NpsdBias = stNS->NpsdBias;
	int frmVAD = stNS->frmVAD;
	int flag_Tx_silence = stNS->flag_Tx_silence;

	// processing
	SV_NS_GetPSD_INT64(EE_bin64, Xtx, HFFTLEN);														// Complete

	Q_EE = GetBlockNorm64(EE_bin64, HEADROOM_NS, HFFTLEN);										// Complete
	Conver64toInt(EE_bin, EE_bin64, Q_EE, MIN_EE, HFFTLEN);										// Complete

	Q_NN = GetBlockNorm64(NN_bin64, HEADROOM_NS, HFFTLEN);										// Complete
	Conver64toInt(NN_bin, NN_bin64, Q_NN, MIN_EE, HFFTLEN);										// Complete

	fn_bin2band(EE, EE_bin, tx_ch_tbl, 1, NUM_CHAN);											// Complete
	fn_bin2band(NN, NN_bin, tx_ch_tbl, 1, NUM_CHAN);											// Complete
	
	// Noise Spectrum Estimation
	Q_Npsd = fn_NoiseSpectrumEstimation(Npsd, Q_Npsd, EE, Q_EE, NN, Q_NN, ph1_GbyL, REchoPSDch, stNS);    // Complete

	// spp
	fn_SPP(spp, EE, Q_EE, Npsd, Q_Npsd, NpsdBias, 1932735283, 0, NUM_CHAN);						// Complete

	// Npsd gain
	gain_Npsd = fn_Npsd_gain(gain_Npsd, flag_Tx_silence);										// Complete

	// decision directed a priori SNR estimation
	fn_decisiondirected_prioriSNR(prioSNR, EE, Q_EE, Npsd, Q_Npsd, gain_Npsd, G_NS);				// Complete

	// apriori SNR smoothing
	fn_prioriSNR_smoothing(prioSNR_sm, prioSNR, NUM_CHAN);/*Q22*/								// Complete

	// noise floor
	fn_noise_floor(band_noise_floor/*Q31*/, prioSNR_sm/*Q22*/, frmVAD, flag_Tx_silence, NUM_CHAN);			// Complete

	// gain
	fn_get_WienerGain(G_NS, prioSNR/*Q22*/, 0.25, NUM_CHAN);											// Complete

	fn_NS_gain_smoothing(G_NS_final, G_NS, ph1_GbyL, spp, band_noise_floor, NUM_CHAN);			// Complete

	// NS fft out
	for (int i = 0; i < 257; i++)G_H1_res[i] = (int)(G_H1_res_double[i] * 2147483647);
    SV_NS_fn_NSoutBin(Xtx, G_NS_final, G_H1_res, tx_ch_tbl, NUM_CHAN);                                                        // Complete

	stNS->FirstFrame = 0;

	return;
}

// SetPar
void SV_NS_SetPar(INT64* Npsdgsc, double* G_H1_res, double* G_REchoPSDch, SV_NS_T* stNS)
{
	//stNS->flagTxsilence=flagTxsilence;
	//stNS->noiselevel=noiselevel;

	return;
}

// Init
extern void SV_NS_Init(int Fs, SV_NS_T* stNS)
{
	int i, j;

	// Init
	stNS->FirstFrame = 1;

	stNS->NpsdBias = 1;
	stNS->frmVAD = 0;
	stNS->noiseLevel = 0;
	stNS->flag_Tx_silence = 0;

	stNS->band_low = 21;

	gain_Npsd = 2147483647;
	noise_floor_offset = 0;

	Q_Npsd = 0;

	for (i = 0; i < NUM_CHAN; i++)
	{
		Npsd[i] = 0;
		Npsd_1st[i] = 0;
		spp[i] = 2147483647;
		spp_1st_smooth[i] = 0;

		for (j = 0; j < Nfrmsnr; j++)
			snr_mtx[j][i] = 0;

		G_NS[i] = 2147483647;
		postSNRprev[i] = 100 << 22;
		prioSNR_avg[i] = 0;
		G_NS_final[i] = 2147483647;

		stNS->G_H1_res[i] = 1;
		stNS->REchoPSDch[i] = 0;
	}

	return;
}

