




#include "SamsungSolomonVoiceW_Int.h"
#include "SamsungSolomonVoiceW_Table.h"
#include "SamsungSolomonVoiceW_Default_functions.h"
#include "SamsungSolomonVoiceW_ANC.h"

#if 0//#ifdef __EXE_ANC__

#ifdef __SV_ANC_DEBUG__
#include "debug_bf.h"
#endif


#define ANC_W_COEF_Q 20

void fn_InterChannelANC_init(InterChnanelANCStatus *anc_vars);
void fn_InterChannelANC_proc(/*out*/short *Out_GSC, /*in*/short *X_FB, short OUTER_BLK_NORM, short *X_BM, short INNER_BLK_NORM, int *W);
short fn_InterChannelANC_ctr(InterChnanelANCStatus *ANC_buf, short *X_main, short OUTER_BLK_NORM, short *X_inner, short *Xenh_inner, short INNER_BLK_NORM, short *ANCFreqUpdate);
void fn_InterChannelANC_UnbaisedMMSE(int *XX, int *Npsd, short BLK_NORM, int *Npsdfast, short *pH1avg, short *pH1, int *postSNR, short Nbin, short Flag_FrmNoiseFree, short Flag_NoiseOnly, short Flag_Tx_silent_mode_BF);
void fn_InterChannelANC_PowerCompen(short *Xenh_inner, int *XXenh_inner, short INNER_BLK_NORM, int *XX_main, short OUTER_BLK_NORM, short *Gavg_inner, int *SNR, short vad, short Rx_vad);
void fn_InterChannelANC_update(/*out*/short *Out_GSC, /*in*/short *X_FB, short OUTER_BLK_NORM, short *X_BM, short INNER_BLK_NORM, int *W, short mu, short updateFrm, short *updateFreq);
int fn_CrossCorrBasedVAD(short *X_main, short OUTER_BLK_NORM, short *X_inner, short INNER_BLK_NORM, short Rx_VAD);
short fn_SNRbasedVAD(int *SNR, short rt, short *pcnt, short Onset_delay, short Offset_delay);
short fn_EbasedVAD(int *E, short blk_norm, short rt, short *pcnt, short Onset_delay, short Offset_delay);
short fn_CepstumbasedVAD(/*out*/short *ps_ceps, /*in*/int *ps, short BLK_NORM, short *pOffset_cnt, short cnt_thd);
short fn_EstNoiseLevel(int Diffmin /*Q16*/, short rt, short *pcnt, short Onset_delay, short Offset_delay);
int fn_TrackEDiffMin(int *XXmain, short main_blk_norm, int *XXenh_inner, short inner_blk_norm, int *Diffsum_win, short *Diffsum_ptr, short vad, short Rx_vad);

short ANCFreqUpdate[DVTX_FFT_HALFLEN_WB/* + 1*/];

const int SNRthd[ANC_FBIN_SIZE] = { 3276800, 3276800, 3276800, 3276800, 3276800, 3276800, 3276800, 3276800, 3276800, 3276800, 3276800, 3276800, 3276800, 3276800, 3276800, 3276800, 3276800, 3276800, 3276800, 3276800, 3276800, 3276800, 3276800, 3276800, 327680, 327680, 327680, 327680, 327680, 327680, 327680, 327680, 327680, 327680, 327680, 327680, 327680, 327680, 327680, 327680, 327680, 327680, 327680, 327680, 327680, 327680, 327680, 327680, 327680, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768,
#if ANC_FBIN_SIZE == 128
16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384
#endif
};
#ifdef __SV_ANC_DEBUG__
short ANC_PowerCompen_update[ANC_FBIN_SIZE] = { 0 };
short ANC_PowerCompen_gain[ANC_FBIN_SIZE] = { 0 };
#endif

InterChnanelANCStatus ANC_vars;
void *SolomonVoiceInterChannelANCInit()
{
	fn_InterChannelANC_init(&ANC_vars);
	return (void*)&ANC_vars;
}

void FN_InterChannelANC_main(DVTX_ECNS_Cfg_t* DVTX_ECNS_vars) {

	FRAMEStatus* FRAME_buf = &DVTX_ECNS_vars->FRAME_buf;
	InterChnanelANCStatus* ANC_buf = (InterChnanelANCStatus*)DVTX_ECNS_vars->ICANC_buf;

	short OUTER_BLK_NORM, INNER_BLK_NORM;
	short mustep;
	short Rx_vad = DVTX_ECNS_vars->AEC_Outer_buf_1.Flag_RxVAD_AEC;
	short UpdateFrm = 1;
	

	int *w_inner = ANC_buf->w_inner;
	short *Xenh_inner = ANC_buf->Xout_inner;
	int *XXenh_inner = ANC_buf->XX_inner;

	short *X_main_tmp = FRAME_buf->fftbuf_Outer_1.data_buffer;
	short *X_main = ANC_buf->X_main_buf;
	int *XX_main = ANC_buf->XX_main;

	short *X_inner = FRAME_buf->fftbuf_Inner.data_buffer;
	short *G_inner = ANC_buf->G_inner;
	short Tx_gain = DVTX_ECNS_vars->FRAME_param.PARA_Gain_TxSig_NS_IN_Outer;
	short Tx_gain_Q = DVTX_ECNS_vars->FRAME_param.PARA_Q_for_InSigGain_Outer;
	short Tx_reverse_gain = 16384;
	short Tx_reverse_gain_Q = 15;
	int i;
	OUTER_BLK_NORM = FRAME_buf->fftbuf_Outer_1.BLK_NORM;
	INNER_BLK_NORM = FRAME_buf->fftbuf_Inner.BLK_NORM;

	Fx_vector_copy_s(X_main, X_main_tmp, ANC_COMP_FBIN_SIZE);
#ifndef SVTX_INPUT_GAIN_DISABLE
	Fx_vector_gain(X_main, Tx_reverse_gain, Tx_reverse_gain_Q, ANC_COMP_FBIN_SIZE); // gain is Q4.11 format... 
#endif

	/* inner mic signal enhancment				*/
	fn_InterChannelANC_proc(Xenh_inner, X_inner, INNER_BLK_NORM, X_main, OUTER_BLK_NORM, w_inner);


	/* update controler							*/
	if (Rx_vad == 1)			  ANC_buf->Rx_VAD = 5;
	else if (ANC_buf->Rx_VAD > 0) ANC_buf->Rx_VAD--;
	UpdateFrm = fn_InterChannelANC_ctr(ANC_buf, X_main, OUTER_BLK_NORM, X_inner, Xenh_inner, INNER_BLK_NORM, ANCFreqUpdate);


	/* beamformer update						*/
	if (UpdateFrm <= 1)		mustep = 327;
	else if(UpdateFrm == 2) mustep = 3276;
	fn_InterChannelANC_update(Xenh_inner, X_inner, INNER_BLK_NORM, X_main, OUTER_BLK_NORM, w_inner, mustep, UpdateFrm, ANCFreqUpdate);

#if 0
	Fx_vector_gain(Xenh_inner, 12953, 12, ANC_COMP_FBIN_SIZE);
	for (int i = 0; i < ANC_FBIN_SIZE; i++) {
		short idx_real = i << 1;
		short idx_imag = idx_real + 1;
		XXenh_inner[i] = Xenh_inner[idx_real] * Xenh_inner[idx_real] + Xenh_inner[idx_imag] * Xenh_inner[idx_imag];
	}
#endif
	/* power adjustment							*/
	fn_InterChannelANC_PowerCompen(Xenh_inner, XXenh_inner, INNER_BLK_NORM, XX_main, OUTER_BLK_NORM, G_inner, ANC_buf->postSNR, ANC_buf->VAD, ANC_buf->Rx_VAD);


	/* Tx input gain compensation				*/
#ifndef SVTX_INPUT_GAIN_DISABLE
	Fx_vector_gain(Xenh_inner, Tx_gain, Tx_gain_Q, ANC_COMP_FBIN_SIZE);
#endif

	// update power term
	for (i = 0; i < ANC_FBIN_SIZE; i++) {
		short idx_real = i * 2;
		short idx_imag = idx_real + 1;
		XXenh_inner[i] = Xenh_inner[idx_real] * Xenh_inner[idx_real] + Xenh_inner[idx_imag] * Xenh_inner[idx_imag];
	}


#ifdef __SV_ANC_DEBUG__
	int N = ANC_FBIN_SIZE;
	if (UpdateFrm == 0) for (i = 0; i < ANC_FBIN_SIZE; i++) { ANCFreqUpdate[i] = 0; }
	fwrite(X_main, sizeof(short), N * 2, f_bf_in[0]);
	fwrite(X_inner, sizeof(short), N * 2, f_bf_in[1]);
	fwrite(Xenh_inner, sizeof(short), N * 2, f_bf_out[0]);
	fwrite(&OUTER_BLK_NORM, sizeof(short), 1, f_bf_in_norm[0]);
	fwrite(&INNER_BLK_NORM, sizeof(short), 1, f_bf_in_norm[1]);
	fwrite(w_inner, sizeof(short), N * 2, f_bf_weight);
	fwrite(ANCFreqUpdate, sizeof(short), N, f_bf_update[0]);
	fwrite(ANC_buf->postSNR, sizeof(int), N, f_bf_mmse[0]);
	fwrite(ANC_buf->H1, sizeof(short), N, f_bf_mmse[1]);
	fwrite(ANC_buf->Npsd, sizeof(int), N, f_bf_mmse[2]);
	fwrite(&(ANC_buf->VAD), sizeof(short), 1, f_bf_Rx_VAD);
	fwrite(&(ANC_buf->NoiseLevel), sizeof(short), 1, f_bf_noiselevel[1]);
	fwrite(ANC_PowerCompen_gain, sizeof(short), N, f_bf_gain_compen[0]);
	fwrite(ANC_PowerCompen_update, sizeof(short), N, f_bf_gain_compen[1]);
#endif

#ifdef __SV_ANC_DEBUG__
#ifdef BSH_ANC_OUT
	for (i = 0; i < ANC_COMP_FBIN_SIZE; i++)
	{
		FRAME_buf->fftbuf_Outer_1.data_buffer[i] = Xenh_inner[i];
	}
	FRAME_buf->fftbuf_Outer_1.BLK_NORM = INNER_BLK_NORM;
#endif
	for (i = 0; i < ANC_COMP_FBIN_SIZE; i++)
	{
		//FRAME_buf->fftbuf_ANC_Out.data_buffer[i] = DVTXOP_shr(Xenh_inner[i], INNER_BLK_NORM - OUTER_BLK_NORM);
		FRAME_buf->fftbuf_ANC_Out.data_buffer[i] = Xenh_inner[i];

	}
	for (; i < 512; i++)
	{
		FRAME_buf->fftbuf_ANC_Out.data_buffer[i] = 0;
	}
	//FRAME_buf->fftbuf_ANC_Out.BLK_NORM = OUTER_BLK_NORM;
	FRAME_buf->fftbuf_ANC_Out.BLK_NORM = INNER_BLK_NORM;

#endif
#ifdef BSH_COPY_ENH_INNER
	for (i = 0; i < ANC_COMP_FBIN_SIZE; i++) FRAME_buf->fftbuf_Inner.data_buffer[i] = Xenh_inner[i];
#endif

}

void fn_InterChannelANC_init(InterChnanelANCStatus *anc_vars)
{	
	int i;
	for (i = 0; i < ANC_COMP_FBIN_SIZE; i++) {
		anc_vars->X_main_buf[i] = 0;
		anc_vars->Xout_inner[i] = 0;
		anc_vars->w_inner[i] = 0;
	}
	for (i = 0; i < ANC_FBIN_SIZE; i++) {
		anc_vars->H1[i] = 0;
		anc_vars->H1avg[i] = 0;
		anc_vars->Npsd[i] = 1;
		anc_vars->Npsdspp[i] = 1;
		anc_vars->XX_inner[i] = 0;
		anc_vars->XX_main[i] = 0;
		anc_vars->postSNR[i] = 0;
		anc_vars->G_inner[i] = 1023;
		anc_vars->G_bfout[i] = 1023;

	}
	anc_vars->Rx_VAD = 0;
	anc_vars->vad_hg_cnt = 0;
	anc_vars->CepsbasedVAD_cnt = 0;
	anc_vars->EbasedVAD_cnt = 0;
	anc_vars->SNRbasedVAD_cnt = 0;
	anc_vars->EbasedVAD = 0;
	anc_vars->SNRbasedVAD= 0;
	anc_vars->BMNpsd = 0;
	
	anc_vars->NoiseLevel = 0;
	anc_vars->NoiseLevel_cnt = 0;
	anc_vars->Diffsum_ptr = 0;
	for (i = 0; i < SV_ANC_DIFF_SUM_WINLEN; i++) anc_vars->Diffsum[i] = -1;

}


void fn_InterChannelANC_proc(/*out*/short *out, /*in*/short *X_FB, short OUTER_BLK_NORM, short *X_BM, short INNER_BLK_NORM, int *W)
{
	short i, idx_real, idx_imag;
	short MICout_q_shift = ANC_W_COEF_Q + INNER_BLK_NORM - 15 - OUTER_BLK_NORM;
	int MICout_real, MICout_imag;
	
	for (i = 0; i < ANC_FBIN_SIZE/* + 1*/; i++)
	{
		idx_real = i << 1;
		idx_imag = idx_real + 1;

		MICout_real = DVTXOP_Ls_mult_r(W[idx_real], X_BM[idx_real]);
		MICout_real -= DVTXOP_Ls_mult_r(W[idx_imag], X_BM[idx_imag]);
		MICout_imag = DVTXOP_Ls_mult_r(W[idx_real], X_BM[idx_imag]);
		MICout_imag += DVTXOP_Ls_mult_r(W[idx_imag], X_BM[idx_real]);
		MICout_real = DVTXOP_L_shr(MICout_real, MICout_q_shift);// Q(OUTER_BLK_NORM)
		MICout_imag = DVTXOP_L_shr(MICout_imag, MICout_q_shift);// Q(OUTER_BLK_NORM)

		out[idx_real] = X_FB[idx_real] - DVTXOP_saturate(MICout_real);
		out[idx_imag] = X_FB[idx_imag] - DVTXOP_saturate(MICout_imag);

	}

	return;
}


void fn_InterChannelANC_PowerCompen(short *Xenh_inner, int *XXenh_inner, short INNER_BLK_NORM, int *XX_main, short OUTER_BLK_NORM, short *Gavg_inner, int *SNR, short vad, short Rx_vad)
{
	short i, idx_real, idx_imag, Stmp1, Stmp2;
	int XXi, XXm;
	int Ltmp;
	short new_g;
	short alpha_inc, alpha_inc_rev, alpha_dec, alpha_dec_rev;
#ifdef __SV_ANC_DEBUG__
	for (i = 0; i < ANC_FBIN_SIZE; i++)
	{
		ANC_PowerCompen_update[i] = 0;
	}
#endif

		if (
		Rx_vad == 0
#ifdef __SV_USE_ANC_BASED_VAD__
			&& vad == SPEECH
#endif
			)
		{
		for (i = 0; i < ANC_FBIN_SIZE; i++)
		{
			XXi = XXenh_inner[i];
			XXm = XX_main[i];

			if (SNR[i] > SNRthd[i])
			{
				alpha_inc = DVTX_WORD16_0_9;
				alpha_inc_rev = DVTX_WORD16_0_1;
				alpha_dec = DVTX_WORD16_0_7;
				alpha_dec_rev = DVTX_WORD16_0_3;
		}
			else if (SNR[i] > SNRthd[i] >> 2)
		{
				alpha_inc = DVTX_WORD16_0_99;
				alpha_inc_rev = DVTX_WORD16_0_01;
				alpha_dec = DVTX_WORD16_0_95;
				alpha_dec_rev = DVTX_WORD16_0_05;
			}
			else
				continue;

			Ltmp = divide(XXm, 2 * OUTER_BLK_NORM, XXi, 2 * INNER_BLK_NORM, 26);
			Ltmp = sqrt32(Ltmp);
			new_g = DVTXOP_saturate(Ltmp);

			if (new_g > Gavg_inner[i])
			{
				Stmp1 = DVTXOP_mult(Gavg_inner[i], alpha_inc);
				Stmp2 = DVTXOP_mult(new_g, alpha_inc_rev);
		}
		else
		{
				Stmp1 = DVTXOP_mult(Gavg_inner[i], alpha_dec);
				Stmp2 = DVTXOP_mult(new_g, alpha_dec_rev);
			}
			Gavg_inner[i] = DVTXOP_add(Stmp1, Stmp2);
#ifdef __SV_ANC_DEBUG__
			ANC_PowerCompen_update[i] = 1;
#endif
		}

		}

	for (i = 0; i < ANC_FBIN_SIZE; i++)
	{
		idx_real = i << 1;
		idx_imag = idx_real + 1;
		Xenh_inner[idx_real] = DVTXOP_saturate(DVTXOP_L_shr(Xenh_inner[idx_real] * Gavg_inner[i], 13));
		Xenh_inner[idx_imag] = DVTXOP_saturate(DVTXOP_L_shr(Xenh_inner[idx_imag] * Gavg_inner[i], 13));

#ifdef __SV_ANC_DEBUG__
		ANC_PowerCompen_gain[i] = Gavg_inner[i];
#endif
	}
}

short fn_InterChannelANC_ctr(InterChnanelANCStatus *ANC_buf, short *X_main, short OUTER_BLK_NORM, short *X_inner, short *Xenh_inner, short INNER_BLK_NORM, short *ANCFreqUpdate)
{
	int i, idx_real, idx_imag;
	int E_main, E_inner;
	int FrmE;
	int thd_silence_detect = 1036215; // 10^(10dB/10) Q15
	int Ediffmin;
	short silence_flag;
	short E_inner_q_shift = (INNER_BLK_NORM - OUTER_BLK_NORM) * 2;
	short E_norm_q = OUTER_BLK_NORM + ANC_FBIN_SIZE_LOG2;
	short UpdateFrm;

	int *XX_main = ANC_buf->XX_main;
	int *XXenh_inner = ANC_buf->XX_inner;
	int *Npsd = ANC_buf->Npsd;
	int *Npsdspp = ANC_buf->Npsdspp;
	int *pSNR = ANC_buf->postSNR;
	short *pH1avg = ANC_buf->H1avg;
	short *pH1 = ANC_buf->H1;
	short Rx_vad = ANC_buf->Rx_VAD;
	short CepsbasedVAD;

#ifdef __ANC_XCORR_BASED_VAD__
	ANC_VAD VAD_xcross;
#endif
	ANC_VAD VAD_final = NONDET;

	FrmE = 0;
	silence_flag = 0;
	UpdateFrm = 1;

	for (i = 0; i < ANC_FBIN_SIZE; i++) {
		idx_real = i << 1;
		idx_imag = idx_real + 1;
		XXenh_inner[i] = Xenh_inner[idx_real] * Xenh_inner[idx_real] + Xenh_inner[idx_imag] * Xenh_inner[idx_imag];
		XX_main[i] = X_main[idx_real] * X_main[idx_real] + X_main[idx_imag] * X_main[idx_imag];
	}

#ifdef __ANC_XCORR_BASED_VAD__
	VAD_xcross = fn_CrossCorrBasedVAD(X_main, OUTER_BLK_NORM, X_inner, INNER_BLK_NORM, Rx_vad);
#endif
	//CepsbasedVAD = fn_CepstumbasedVAD(ANC_buf->ceps_inner, XXenh_inner, 2 * INNER_BLK_NORM, &(ANC_buf->CepsbasedVAD_cnt), 25);
#ifdef __SV_USE_ANC_BASED_VAD__
	ANC_buf->EbasedVAD = fn_EbasedVAD(XXenh_inner, INNER_BLK_NORM * 2, ANC_buf->EbasedVAD, &(ANC_buf->EbasedVAD_cnt), 0, 20);
	ANC_buf->SNRbasedVAD = fn_SNRbasedVAD(pSNR, ANC_buf->SNRbasedVAD, &(ANC_buf->SNRbasedVAD_cnt), 3, 10);
#endif

	if (Rx_vad > 0) {
		VAD_final = Rx_ON;
		UpdateFrm = 0;
#ifdef __ANC_XCORR_BASED_VAD__
		ANC_buf->vad_hg_cnt = 0;
#endif
		for (i = 0; i < ANC_FBIN_SIZE; i++) {
			pH1[i] = 0;
			pH1avg[i] = 0;
			pSNR[i] = 0;
		}
	}
	else
	{
#ifdef __SV_USE_ANC_BASED_VAD__
		if ((ANC_buf->EbasedVAD == 1 || ANC_buf->SNRbasedVAD == 1)) VAD_final = SPEECH;
#endif
		for (i = 0; i < ANC_FBIN_SIZE; i++) {
			FrmE += DVTXOP_L_shr_r(XX_main[i], E_norm_q); // mean(E_main), Q(OUTER_BLK_NORM)
		}
		thd_silence_detect = DVTXOP_L_shr_r(thd_silence_detect, 15 - OUTER_BLK_NORM);
		silence_flag = (FrmE < thd_silence_detect) ? 1 : 0;

#ifdef __ANC_XCORR_BASED_VAD__
		if (VAD_xcross == NOISE) ANC_buf->vad_hg_cnt++;
		else ANC_buf->vad_hg_cnt = 0;
		if (ANC_buf->vad_hg_cnt > 20) {
			VAD_final = NOISE;
			ANC_buf->vad_hg_cnt = 21;
		}
		fn_InterChannelANC_UnbaisedMMSE(XXenh_inner, Npsd, INNER_BLK_NORM, Npsdspp, pH1avg, pH1, pSNR, ANC_FBIN_SIZE, 0, VAD_final == NOISE, silence_flag);
#else
		fn_InterChannelANC_UnbaisedMMSE(XXenh_inner, Npsd, INNER_BLK_NORM, Npsdspp, pH1avg, pH1, pSNR, ANC_FBIN_SIZE, 0, 0, silence_flag);
#endif


		for (i = 0; i < ANC_FBIN_SIZE; i++) {
			E_main = XX_main[i];
			E_inner = XXenh_inner[i];
			E_inner = DVTXOP_L_shr_r(E_inner, E_inner_q_shift);

			// energy difference based
			ANCFreqUpdate[i] = (E_inner > E_main) ? 0 : 1;

			// spp based
			if (pH1[i] > 20000) ANCFreqUpdate[i] = 0;
		}
	}
	ANC_buf->VAD = VAD_final;
	UpdateFrm = UpdateFrm && !silence_flag;
	if (VAD_final == NOISE && UpdateFrm == 1) UpdateFrm = 2;
	
	Ediffmin = fn_TrackEDiffMin(XX_main, OUTER_BLK_NORM * 2, XXenh_inner, INNER_BLK_NORM * 2, ANC_buf->Diffsum, &(ANC_buf->Diffsum_ptr), ANC_buf->VAD, Rx_vad);
	ANC_buf->NoiseLevel = fn_EstNoiseLevel(Ediffmin, ANC_buf->NoiseLevel, &(ANC_buf->NoiseLevel_cnt), 20, 20);

	return UpdateFrm;
}


short fn_InterChannelANC_ctr2(InterChnanelANCStatus *ANC_buf, short *X_main, short OUTER_BLK_NORM, short *X_inner, short *Xenh_inner, short INNER_BLK_NORM, short *ANCFreqUpdate)
{
	int i, idx_real, idx_imag;
	int E_main, E_inner;
	int FrmE;
	int thd_silence_detect = 1036215; // 10^(10dB/10) Q15
	short silence_flag;
	short E_inner_q_shift = (INNER_BLK_NORM - OUTER_BLK_NORM) * 2;
	short E_norm_q = OUTER_BLK_NORM + ANC_FBIN_SIZE_LOG2;
	short UpdateFrm;

	int *XXi = ANC_buf->XX_inner;
	int *Npsd = ANC_buf->Npsd;
	int *Npsdspp = ANC_buf->Npsdspp;
	int *pSNR = ANC_buf->postSNR;
	short *pH1avg = ANC_buf->H1avg;
	short *pH1 = ANC_buf->H1;
	short Rx_vad = ANC_buf->Rx_VAD;

	ANC_VAD VAD_CC;
	ANC_VAD VAD_final = NONDET;

	FrmE = 0;
	silence_flag = 0;
	UpdateFrm = 1;

	VAD_CC = fn_CrossCorrBasedVAD(X_main, OUTER_BLK_NORM, X_inner, INNER_BLK_NORM, Rx_vad);

	if (Rx_vad > 0) {
		VAD_final = Rx_ON;
		UpdateFrm = 0;
		ANC_buf->vad_hg_cnt = 0;
		for (i = 0; i < ANC_FBIN_SIZE; i++) {
			pH1[i] = 0;
			pH1avg[i] = 0;
			pSNR[i] = 0;
		}
	}
	else
	{
		if (VAD_CC == NOISE) ANC_buf->vad_hg_cnt++;
		else ANC_buf->vad_hg_cnt = 0;
		if (ANC_buf->vad_hg_cnt > 20) {
			VAD_final = NOISE;
			ANC_buf->vad_hg_cnt = 21;
		}

		for (i = 0; i < ANC_FBIN_SIZE; i++) {
			idx_real = i << 1;
			idx_imag = idx_real + 1;
			XXi[i] = Xenh_inner[idx_real] * Xenh_inner[idx_real] + Xenh_inner[idx_imag] * Xenh_inner[idx_imag];
		}
		fn_InterChannelANC_UnbaisedMMSE(XXi, Npsd, INNER_BLK_NORM, Npsdspp, pH1avg, pH1, pSNR, ANC_FBIN_SIZE, 0, 0, silence_flag);

		for (i = 0; i < ANC_FBIN_SIZE; i++) {
			idx_real = i << 1;
			idx_imag = idx_real + 1;

			E_main = X_main[idx_real] * X_main[idx_real] + X_main[idx_imag] * X_main[idx_imag];
			E_inner = XXi[i];
			E_inner = DVTXOP_L_shr_r(E_inner, E_inner_q_shift);
			ANCFreqUpdate[i] = (E_inner > E_main) ? 0 : 1;
			if (pH1[i] > 20000)
				ANCFreqUpdate[i] = 0;
			FrmE += DVTXOP_L_shr_r(E_main, E_norm_q); // mean(E_main), Q(OUTER_BLK_NORM)
		}
		thd_silence_detect = DVTXOP_L_shr_r(thd_silence_detect, 15 - OUTER_BLK_NORM);
		silence_flag = (FrmE < thd_silence_detect) ? 1 : 0;
		//if (silence_flag == 1) VAD_final = SIL;
	}
	//printf("       %d\n", VAD_final);
	ANC_buf->VAD = VAD_final;
	//UpdateFrm = UpdateFrm && (VAD_final == NOISE || VAD_final == NONDET);
	UpdateFrm = UpdateFrm && !silence_flag;
	if (VAD_final == NOISE && UpdateFrm == 1) UpdateFrm = 2;

	//UpdateFrm = UpdateFrm && !silence_flag && (VAD_final == NOISE);
	return UpdateFrm;
}

void fn_InterChannelANC_update(/*out*/short *out, /*in*/short *X_FB, short OUTER_BLK_NORM, short *X_BM, short INNER_BLK_NORM, int *W, short mu, short updateFrm, short *updateFreq)
{
	short i, idx_real, idx_imag;
	int Ltmp, thr;
	int PwrAvg;
	int deltaW[2], XXbm, PwrDSVal;
	int normW;
	short XXbm_q = 2 * INNER_BLK_NORM;
	short Gradient_q = (INNER_BLK_NORM + OUTER_BLK_NORM);

	if (updateFrm > 0)
	{
		for (i = 0; i < ANC_FBIN_SIZE/* + 1*/; i++)
		{
			idx_real = i << 1;
			idx_imag = idx_real + 1;

			XXbm = X_BM[idx_real] * X_BM[idx_real] + X_BM[idx_imag] * X_BM[idx_imag];
			//Q(INNER_BLK_NORM)*Q(INNER_BLK_NORM) => Q(2*INNER_BLK_NORM)

			//thr = DVTXOP_L_shl(1, (2 * INNER_BLK_NORM - 16)); //Q(2 * INNER_BLK_NORM - 16)
			thr = 1;
			XXbm = (XXbm < thr) ? thr : XXbm;

			PwrAvg = 0;
			PwrDSVal = DVTXOP_L_add(XXbm, PwrAvg); //Q(2*INNER_BLK_NORM+1)

			if (updateFreq[i] == 1)
			{
				// real value
				//Q(INNER_BLK_NORM)*Q(OUTER_BLK_NORM) => Q(INNER_BLK_NORM+OUTER_BLK_NORM)
				Ltmp = X_BM[idx_real] * out[idx_real] + X_BM[idx_imag] * out[idx_imag];
				//Q(INNER_BLK_NORM+OUTER_BLK_NORM) * Q(0.15) => Q(INNER_BLK_NORM+OUTER_BLK_NORM)
#ifndef DIVIDE_TEST4
				deltaW[0] = divide(Ltmp, Gradient_q, PwrDSVal, XXbm_q, ANC_W_COEF_Q);
#else
				deltaW[0] = DVTXOP_divide_sdivlimit15(Ltmp, PwrDSVal);
				deltaW[0] = DVTXOP_L_shr(deltaW[0], 2/*15 - ANC_W_COEF_Q*/);
#endif
				deltaW[0] = DVTXOP_L_mpy_ls(deltaW[0], mu);

				// imaginary value
				//Q(INNER_BLK_NORM)*Q(OUTER_BLK_NORM) => Q(INNER_BLK_NORM+OUTER_BLK_NORM)
				Ltmp = X_BM[idx_real] * out[idx_imag] - X_BM[idx_imag] * out[idx_real];
				//Q(INNER_BLK_NORM+OUTER_BLK_NORM) * Q(0.15) => Q(INNER_BLK_NORM+OUTER_BLK_NORM)
#ifndef DIVIDE_TEST4
				deltaW[1] = divide(Ltmp, Gradient_q, PwrDSVal, XXbm_q, ANC_W_COEF_Q);
#else
				deltaW[1] = DVTXOP_divide_sdivlimit15(Ltmp, PwrDSVal);
				deltaW[1] = DVTXOP_L_shr(deltaW[1], 2/*15 - ANC_W_COEF_Q*/);
#endif
				deltaW[1] = DVTXOP_L_mpy_ls(deltaW[1], mu);

			}
			else
			{
				deltaW[0] = 0;
				deltaW[1] = 0;
			}

			normW = deltaW[0] * deltaW[0] + deltaW[1] * deltaW[1];

			if (normW < 67108864 /*0dB(Q30)*/)
			{
				W[idx_real] = DVTXOP_L_add(W[idx_real], deltaW[0]);//Q(ANC_W_COEF_Q)
				W[idx_imag] = DVTXOP_L_add(W[idx_imag], deltaW[1]);//Q(ANC_W_COEF_Q)
			}
#ifdef __SV_ANC_DEBUG__
			else
			{
				updateFreq[i] = 0;
			}
#endif


		}
	}
}


void fn_InterChannelANC_UnbaisedMMSE(int *XX, int *Npsd, short BLK_NORM, int *Npsdfast, short *pH1avg, short *pH1, int *postSNR, short Nbin, short Flag_FrmNoiseFree, short Flag_NoiseOnly, short Flag_Tx_silent_mode_BF)
{
	short tmp, i, q1, qXX;
	int Ltmp, alpha, beta;
	short Npsd_q = 8;
	// Npsd Q8
	qXX = 2 * BLK_NORM;
	for (i = 0; i < Nbin; i++)
	{
		// postSNR = |X|^2 / Npsd
		// SPP = spptable2_BF(postSNR)
		q1 = DVTXOP_norm_l(Npsd[i]);
		Ltmp = DVTXOP_L_shl(Npsd[i], q1);
		tmp = DVTXOP_div_s((short)0x3fff, DVTXOP_extract_h(Ltmp));
		Ltmp = DVTXOP_Ls_mult_r(XX[i], tmp); //Q(qXX+45-Npsd_q-q1-15) = Q(qXX + 30 - Npsd_q - q1)
		postSNR[i] = DVTXOP_L_shr(Ltmp, qXX - Npsd_q - q1 + 20); //Q10
		Ltmp = DVTXOP_L_shr(Ltmp, qXX - Npsd_q - q1 + 27); //Q3
		Ltmp = DVTX_MIN(DVTX_MAX(Ltmp, 1), 127);
		pH1[i] = spptable_BF[Ltmp - 1];
	}

	for (i = 0; i < Nbin; i++)
	{
		// SPPavg = 0.9 * SPPavg + 0.1 * SPP
		// if SPPavg > 0.99 and SPP > 0.99  SPP->0.99
		pH1avg[i] = DVTXOP_extract_h(DVTXOP_L_add(DVTXOP_L_mult(pH1avg[i], 29491), DVTXOP_L_mult(pH1[i], 3277))); //Q15
		if (pH1avg[i] >= DVTX_WORD16_0_99 && pH1[i] > DVTX_WORD16_0_99) pH1[i] = DVTX_WORD16_0_99;

		// Npsdfast = SPP * Npsdfast + (1-SPP)* |X|^2
		Ltmp = DVTXOP_L_shr(DVTXOP_L_mult_r(XX[i], DVTXOP_L_sub(2147483647, DVTXOP_L_deposit_h(pH1[i]))), qXX - Npsd_q);
		Npsdfast[i] = DVTXOP_L_add(Ltmp, DVTXOP_L_mult_r(Npsdfast[i], DVTXOP_L_deposit_h(pH1[i])));
	}


	if (Flag_Tx_silent_mode_BF == 1) beta = 1503238553; //0.7
	else beta = 1975684956;//0.94
	//beta = 1825361099;//Q31, 0.85	       


	alpha = DVTXOP_L_sub(2147483647, beta);
	for (i = 0; i < Nbin; i++)
	{
		// Npsd = beta * Npsd + (1-beta) * Npsdfast
		// if silent beta -> 0.7 
		// else beta -> 0.94
		Npsd[i] = DVTXOP_L_add(DVTXOP_L_mult_r(Npsd[i], beta), DVTXOP_L_mult_r(Npsdfast[i], alpha));
		if (Npsd[i] < 4) Npsd[i] = 4;
	}

	if ((Flag_FrmNoiseFree == 1))
	{
		// Npsd = beta * Npsd + (1-beta) * |X|^2
		// beta -> 0.99
		beta = 2126008811; alpha = DVTXOP_L_sub(2147483647, beta);
		for (i = 0; i < Nbin; i++)
		{
			Ltmp = DVTXOP_L_shr(DVTXOP_L_mult_r(XX[i], alpha), qXX - Npsd_q);
			Npsd[i] = DVTXOP_L_add(Ltmp, DVTXOP_L_mult_r(Npsd[i], beta));
			if (Npsd[i] < 4) Npsd[i] = 4;
		}
	}

	if ((Flag_NoiseOnly == 1))
	{
		// Npsd = beta * Npsd + (1-beta) * |X|^2
		// beta -> 0.8
		beta = 1717986918; alpha = DVTXOP_L_sub(2147483647, beta);
		for (i = 0; i < Nbin; i++)
		{
			Ltmp = DVTXOP_L_shr(DVTXOP_L_mult_r(XX[i], alpha), qXX - Npsd_q);
			Npsd[i] = DVTXOP_L_add(Ltmp, DVTXOP_L_mult_r(Npsd[i], beta));
			if (Npsd[i] < 4) Npsd[i] = 4;
		}
	}

	return;
}

int fn_CrossCorrBasedVAD(short *X_main, short OUTER_BLK_NORM, short *X_inner, short INNER_BLK_NORM, short Rx_VAD)
{
	short i, idx_real, idx_imag;
	short head_room = 3, norm_q;
	int Ltmp, Ltmp1, Ltmp2;
	int Lcc_r, Lcc_i, Lcc_r_sum, Lcc_i_sum, Lcc_norm1, Lncc, Lcc_sum_abs;
	short Scc_r_sum, Scc_i_sum;
	ANC_VAD VAD = NONDET;
	Lcc_r_sum = Lcc_i_sum = Lcc_norm1 = 0;

	if (Rx_VAD == 0)
	{
		for (i = 10; i < 31; i++) {
			idx_real = i << 1;
			idx_imag = idx_real + 1;

			Lcc_r = X_main[idx_real] * X_inner[idx_real] + X_main[idx_imag] * X_inner[idx_imag]; // Q(OUTER_BLK_NORM+INNER_BLK_NORM)
			Lcc_i = X_main[idx_imag] * X_inner[idx_real] - X_main[idx_real] * X_inner[idx_imag];
			Lcc_r = DVTXOP_L_shr(Lcc_r, INNER_BLK_NORM + head_room);
			Lcc_i = DVTXOP_L_shr(Lcc_i, INNER_BLK_NORM + head_room);
			Lcc_r_sum += Lcc_r;  // Q(OUTER_BLK_NORM - head_room)
			Lcc_i_sum += Lcc_i;

			Lcc_r = bound(Lcc_r, -32767, 32767);
			Lcc_i = bound(Lcc_i, -32767, 32767);

			Ltmp = Lcc_r * Lcc_r + Lcc_i * Lcc_i;  // Q(2 * (OUTER_BLK_NORM - head_room))
#ifdef __SV_ANC_DEBUG__
			if (Ltmp < 0)
				printf("Overflow!! --> fn_CrossCorrBasedVAD :: crosscorr^2 \n");
#endif

			Ltmp = sqrt32(Ltmp);
			Lcc_norm1 += Ltmp;  // Q(OUTER_BLK_NORM - head_room);
		}

		Ltmp1 = DVTXOP_L_abs(Lcc_r_sum); Ltmp2 = DVTXOP_L_abs(Lcc_i_sum);
		Ltmp = (Ltmp1 > Ltmp2) ? Ltmp1 : Ltmp2;
		norm_q = DVTXOP_norm_l(Ltmp) - 1;
		Scc_r_sum = DVTXOP_extract_l(DVTXOP_L_shl(Lcc_r_sum, norm_q - 16)); // Q(OUTER_BLK_NORM - head_room + norm_q - 16);
		Scc_i_sum = DVTXOP_extract_l(DVTXOP_L_shl(Lcc_i_sum, norm_q - 16));
		Lcc_sum_abs = Scc_r_sum * Scc_r_sum + Scc_i_sum * Scc_i_sum; // Q(2*(OUTER_BLK_NORM - head_room + norm_q - 16));
		Lcc_sum_abs = sqrt32(Lcc_sum_abs);  // Q(OUTER_BLK_NORM - head_room + norm_q - 16);
		Lncc = divide(Lcc_sum_abs, norm_q - 16, Lcc_norm1, 0, 31);
	}
	else
	{
		Lncc = 0;
	}
	if (Lncc > 1932735283)  //0.9 Q31
		VAD = NOISE;

#ifdef __SV_ANC_DEBUG__
	fwrite(&Lncc, sizeof(int), 1, f_bf_normcross);
#endif
	return VAD;
}

#if 0
short fn_CepstumbasedVAD(/*out*/short *ps_ceps, /*in*/int *ps, short BLK_NORM, short *pOffset_cnt, short cnt_thd)
{
	/*
	(IN) ps : Q(2 * BLK_NORM )
	(OUT) ps_ceps : Q(2*BLK_NORM- 8)
	*/
	short rt = 0;
	int i;
	int ps_log[64]/*[DVTX_FFT_HALFLEN_WB + 1]*/;
	int max_ceps;

	for (i = 0; i < 64; i++)
	{
		//ps_log[i] = (float)log((float)ps[i]); // error < 1e-06
		ps_log[i] = DVTXOP_10log10_l(ps[i], BLK_NORM);  //Q23
		//ps_log[i] = DVTXOP_L_sub(DVTXOP_fnLog10(ps[i]), DVTX_LOG10FIX);//log10(ps[i]) => Q(26)

		//tmpFFTvec[2 * i] = (float)ps_log[i]; // error < 1e-06
		ps_ceps[2 * i] = DVTXOP_extract_h(ps_log[i]) > -3840 ? DVTXOP_extract_h(ps_log[i]) : -3840; // Q(7)
		ps_ceps[2 * i] = DVTXOP_shr(ps_ceps[2 * i], 2);
		ps_ceps[2 * i + 1] = 0;
	}

	//r_fft(tmpFFTvec, -1);
	tx_r_fft_128(ps_ceps, -1);

	max_ceps = 0;
	for (i = 10; i < 64; i++)
	{
		if (max_ceps < ps_ceps[i]) max_ceps = ps_ceps[i];
	}
	if (max_ceps > 8000) *pOffset_cnt = cnt_thd;
	else (*pOffset_cnt)--;

	if (*pOffset_cnt > 0) rt = 1;


	return rt;

}
#endif
short fn_EbasedVAD(int *E, short blk_norm, short rt, short *pcnt, short Onset_delay, short Offset_delay)
{
	int i;
	int Esum = 0; // Q9
	int E_Offset_thd = 161908616; // 55dB
	int E_Onset_thd = 161908616; // 55dB

	for (i = 5; i < 50; i++)
	{
		Esum = DVTXOP_L_add(Esum, DVTXOP_L_shr(E[i], blk_norm - 9)); // it will be saturated at 66.22dB
	}

	if (rt == 1)
	{
		if (Esum < E_Offset_thd) (*pcnt)++;
		else *pcnt = 0;

		if (*pcnt == Offset_delay + 1) { // vad 1 -> 0
			rt = 0;
			*pcnt = 0;
		}
	}
	else // rt == 0
	{
		if (Esum > E_Onset_thd) (*pcnt)++;
		else *pcnt = 0;

		if (*pcnt == Onset_delay + 1) // vad 0 -> 1
		{
			rt = 1;
			*pcnt = 0;
		}
	}

#ifdef __SV_ANC_DEBUG__
	fwrite(&Esum, sizeof(int), 1, f_bf_VAD[0]);
#endif
	return rt;
}
short fn_SNRbasedVAD(int *SNR, short rt, short *pcnt, short Onset_delay, short Offset_delay)
{
	int i;
	int SNRsum = 0; // Q15
	int SNR_Offset_thd = 327680; // 10dB
	int SNR_Onset_thd = 3276800; // 20dB
	//32768000; / 30dB
	//10362151; // 25dB
	//3276800; // 20dB
	//1036215; // 15dB
	//327680; // 10dB

	for (i = 5; i < 50; i++)
	{
		SNRsum = DVTXOP_L_add(SNRsum, SNR[i]); // it will be saturated at 45.15dB
	}

	if (rt == 1)
	{
		if (SNRsum < SNR_Offset_thd) (*pcnt)++;
		else *pcnt = 0;

		if (*pcnt == Offset_delay + 1) { // vad 1 -> 0
			rt = 0;
			*pcnt = 0;
		}
	}
	else // rt == 0
	{
		if (SNRsum > SNR_Onset_thd) (*pcnt)++;
		else *pcnt = 0;

		if (*pcnt == Onset_delay + 1) // vad 0 -> 1
		{
			rt = 1;
			*pcnt = 0;
		}
	}

#ifdef __SV_ANC_DEBUG__
	fwrite(&SNRsum, sizeof(int), 1, f_bf_VAD[1]);
#endif
	return rt;
}

short fn_EstNoiseLevel(int Diffmin /*Q16*/, short rt, short *pcnt, short Onset_delay, short Offset_delay)
{
	int i;
	int Diffmin_Offset_thd = 65536000; // 30dB
	int Diffmin_Onset_thd = 65536000; // 30dB

	if (rt == 1)
	{
		if (Diffmin < Diffmin_Offset_thd) (*pcnt)++;
		else *pcnt = 0;

		if (*pcnt == Offset_delay + 1) { // vad 1 -> 0
			rt = 0;
			*pcnt = 0;
		}
	}
	else // rt == 0
	{
		if (Diffmin > Diffmin_Onset_thd) (*pcnt)++;
		else *pcnt = 0;

		if (*pcnt == Onset_delay + 1) // vad 0 -> 1
		{
			rt = 1;
			*pcnt = 0;
		}
	}

	return rt;
}

int fn_TrackEDiffMin(int *XXmain, short main_blk_norm, int *XXenh_inner, short inner_blk_norm, int *Diffsum_win, short *Diffsum_ptr, short vad, short Rx_vad)
{
	int i;
	int Diffsum = 0; // Q16
	int Diffsum_min; // Q16
	int Ltmp1, Ltmp2;
	if (vad != SPEECH && Rx_vad == 0) {
		for (i = 5; i < 64; i++) {
			Ltmp1 = DVTXOP_L_shr(XXmain[i], main_blk_norm - 16);
			Ltmp2 = DVTXOP_L_shr(XXenh_inner[i], inner_blk_norm - 16);
			Ltmp1 = DVTXOP_L_sub(Ltmp1, Ltmp2);
			if (Ltmp1 < 0) Ltmp1 = 0;
			Diffsum = DVTXOP_L_add(Diffsum, Ltmp1); // it will be saturated at 45.12dB
		}

		if(Diffsum > 0) Diffsum_win[*Diffsum_ptr] = Diffsum;
		(*Diffsum_ptr)++;
		if (*Diffsum_ptr >= SV_ANC_DIFF_SUM_WINLEN) (*Diffsum_ptr) = 0;
	}

	Diffsum_min = Diffsum_win[0];
	for (i = 1; i < SV_ANC_DIFF_SUM_WINLEN; i++)
	{
		if (Diffsum_win[i] < Diffsum_min && Diffsum_win[i] >= 0) Diffsum_min = Diffsum_win[i];
	}

#ifdef __SV_ANC_DEBUG__
	printf("        Diff: %f\n", Diffsum/ 65536);
#endif

#ifdef __SV_ANC_DEBUG__
	fwrite(&Diffsum_min, sizeof(int), 1, f_bf_noiselevel[0]);
#endif	
	return Diffsum_min;
}


#endif