/*
 * Project 25 IMBE Encoder/Decoder Fixed-Point implementation
 * Developed by Pavel Yazev E-mail: pyazev@gmail.com
 * Version 1.0 (c) Copyright 2009
 * 
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3, or (at your option)
 * any later version.
 * 
 * The software is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this; see the file COPYING.  If not, write to the Free
 * Software Foundation, Inc., 51 Franklin Street, Boston, MA
 * 02110-1301, USA.
 */


#include "typedef.h"
#include "basic_op.h"
#include "imbe.h"
#include "aux_sub.h"
#include "dsp_sub.h"
#include "math_sub.h"
#include "v_synt.h"
#include "rand_gen.h"
#include "tbls.h"
#include "encode.h"
#include "imbe_vocoder_impl.h"






#define CNST_0_1_Q1_15 0x0CCD



void imbe_vocoder_impl::v_synt_init(void)
{
	Word16 i;

	for(i = 0; i < NUM_HARMS_MAX; i++)
	{
		ph_mem[i] = L_deposit_h(rand_gen());
		vu_dsn_prev[i] = 0;
	}

	num_harms_prev3 = 0;
	fund_freq_prev = 0;
}


void imbe_vocoder_impl::v_synt(IMBE_PARAM *imbe_param, Word16 *snd)
{
	Word32 L_tmp, L_tmp1, fund_freq, L_snd[FRAME], L_ph_acc, L_ph_step;
	Word32 L_ph_acc_aux, L_ph_step_prev, L_amp_acc, L_amp_step, L_ph_step_aux;
	Word16 num_harms, i, j, *vu_dsn, *sa, *s_ptr, *s_ptr_aux, num_harms_max, num_harms_max_4;
	UWord32 ph_mem_prev[NUM_HARMS_MAX], dph[NUM_HARMS_MAX];
	Word16 num_harms_inv, num_harms_sh, num_uv;
	Word16 freq_flag;


	fund_freq = imbe_param->fund_freq;
	num_harms = imbe_param->num_harms;
	vu_dsn    = imbe_param->v_uv_dsn;
	sa        = imbe_param->sa;
	num_harms_inv = imbe_param->div_one_by_num_harm;
	num_harms_sh  = imbe_param->div_one_by_num_harm_sh;
	num_uv        = imbe_param->l_uv;

	for(i = 0; i < FRAME; i++)
		L_snd[i] = 0;
	
	// Update phases (calculated phase value correspond to bound of frame)
	L_tmp = (((fund_freq_prev + fund_freq) >> 7) * FRAME/2) << 7;  // It is performed integer multiplication by mod 1

	L_ph_acc = 0;
	for(i = 0; i < NUM_HARMS_MAX; i++)
	{
		ph_mem_prev[i] = ph_mem[i];
		L_ph_acc  += L_tmp;
		ph_mem[i] += L_ph_acc;
		dph[i] = 0;
	}

	num_harms_max = (num_harms >= num_harms_prev3)?num_harms:num_harms_prev3;
    num_harms_max_4 = num_harms_max >> 2;

	if(L_abs(L_sub(fund_freq, fund_freq_prev)) >= L_mpy_ls(fund_freq, CNST_0_1_Q1_15))
		freq_flag = 1;
	else
		freq_flag = 0;

	L_ph_step = L_ph_step_prev = 0;
	for(i = 0; i < num_harms_max; i++)
	{
		L_ph_step      += fund_freq;
		L_ph_step_prev += fund_freq_prev;


		if(i > num_harms_max_4)
		{
			if(num_uv == num_harms)
			{
				dph[i] = L_deposit_h(rand_gen());				
			}
			else
			{			
				L_tmp = L_mult(rand_gen(), num_harms_inv); 
				dph[i] = L_shr(L_tmp, 15 - num_harms_sh) * num_uv;
			}
			ph_mem[i] += dph[i];
		}

		if(vu_dsn[i] == 0 && vu_dsn_prev[i] == 0)
			continue;

		if(vu_dsn[i] == 1 && vu_dsn_prev[i] == 0)  // unvoiced => voiced
		{
			s_ptr = (Word16 *)ws;
			L_ph_acc = ph_mem[i] - (((L_ph_step >> 7) * 104) << 7);
			for(j = 56; j <= 104; j++)
			{
				L_tmp = L_mult(*s_ptr++, sa[i]);
				L_tmp = L_mpy_ls(L_tmp, cos_fxp(extract_h(L_ph_acc)));
				L_tmp = L_shr(L_tmp, 1);
				L_snd[j] = L_add(L_snd[j], L_tmp);
				L_ph_acc += L_ph_step;
			}

			for(j = 105; j <= 159; j++)
			{
				L_tmp = L_mult(sa[i], cos_fxp(extract_h(L_ph_acc)));
				L_tmp = L_shr(L_tmp, 1);
				L_snd[j] = L_add(L_snd[j], L_tmp);
				L_ph_acc += L_ph_step;
			}
			continue;
		}

 
		if(vu_dsn[i] == 0 && vu_dsn_prev[i] == 1)  // voiced => unvoiced
		{
			s_ptr = (Word16 *)&ws[48];
			L_ph_acc = ph_mem_prev[i];

			for(j = 0; j <= 55; j++)
			{
				L_tmp = L_mult(sa_prev3[i], cos_fxp(extract_h(L_ph_acc)));
				L_tmp = L_shr(L_tmp, 1);
				L_snd[j] = L_add(L_snd[j], L_tmp);
				L_ph_acc += L_ph_step_prev;
			}

			for(j = 56; j <= 104; j++)
			{
				L_tmp = L_mult(*s_ptr--, sa_prev3[i]);
				L_tmp = L_mpy_ls(L_tmp, cos_fxp(extract_h(L_ph_acc)));
				L_tmp = L_shr(L_tmp, 1);
				L_snd[j] = L_add(L_snd[j], L_tmp);
				L_ph_acc += L_ph_step_prev;
			}
			continue;
		}

		if(i >=7 || freq_flag)
		{
			s_ptr_aux     = (Word16 *)&ws[48];
			L_ph_acc_aux  = ph_mem_prev[i];

			s_ptr    = (Word16 *)ws;
			L_ph_acc = ph_mem[i] - (((L_ph_step >> 7) * 104) << 7);

			for(j = 0; j <= 55; j++)
			{
				L_tmp = L_mult(sa_prev3[i], cos_fxp(extract_h(L_ph_acc_aux)));
				L_tmp = L_shr(L_tmp, 1);
				L_snd[j] = L_add(L_snd[j], L_tmp);
				L_ph_acc_aux += L_ph_step_prev;
			}

			for(j = 56; j <= 104; j++)
			{
				L_tmp = L_mult(*s_ptr_aux--, sa_prev3[i]);
				L_tmp = L_mpy_ls(L_tmp, cos_fxp(extract_h(L_ph_acc_aux)));
				L_tmp = L_shr(L_tmp, 1);
				L_snd[j] = L_add(L_snd[j], L_tmp);

				L_tmp = L_mult(*s_ptr++, sa[i]);
				L_tmp = L_mpy_ls(L_tmp, cos_fxp(extract_h(L_ph_acc)));
				L_tmp = L_shr(L_tmp, 1);
				L_snd[j] = L_add(L_snd[j], L_tmp);

				L_ph_acc_aux += L_ph_step_prev;
				L_ph_acc += L_ph_step;
			}

			for(j = 105; j <= 159; j++)
			{
				L_tmp = L_mult(sa[i], cos_fxp(extract_h(L_ph_acc)));
				L_tmp = L_shr(L_tmp, 1);
				L_snd[j] = L_add(L_snd[j], L_tmp);
				L_ph_acc += L_ph_step;
			}
			continue;
		}
	
		L_amp_step = L_mpy_ls(L_shr(L_deposit_h(sub(sa[i], sa_prev3[i])), 4 + 1), CNST_0_1_Q1_15); // (sa[i] - sa_prev3[i]) / 160, 1/160 = 0.1/16 
		L_amp_acc  = L_shr(L_deposit_h(sa_prev3[i]), 1);

		
		L_ph_step_aux = L_mpy_ls(L_shr(fund_freq - fund_freq_prev, 4 + 1), CNST_0_1_Q1_15);       // (fund_freq - fund_freq_prev)/(2*160)
		L_ph_step_aux = ((L_ph_step_aux >> 7) * (i + 1)) << 7;

		L_ph_acc = ph_mem_prev[i];

		L_tmp1 = L_mpy_ls(L_shr(dph[i], 4), CNST_0_1_Q1_15);  // dph[i] / 160
					
		for(j = 0; j < 160; j++)
		{
			L_ph_acc_aux = ((L_ph_step_aux >> 9) * j) << 9; 
			L_ph_acc_aux = ((L_ph_acc_aux >> 9) * j) << 9; 

			L_tmp = L_mpy_ls(L_amp_acc, cos_fxp(extract_h(L_ph_acc + L_ph_acc_aux)));			
			L_snd[j] = L_add(L_snd[j], L_tmp);

			L_amp_acc = L_add(L_amp_acc, L_amp_step);
			L_ph_acc += L_ph_step_prev;
			L_ph_acc += L_tmp1;
		}
	}

	for(i = 0; i < FRAME; i++)
		*snd++ = extract_h(L_snd[i]);

	v_zap(vu_dsn_prev, NUM_HARMS_MAX);
	v_equ(vu_dsn_prev, imbe_param->v_uv_dsn, num_harms);
	v_equ(sa_prev3, imbe_param->sa, num_harms);

	num_harms_prev3 = num_harms;
	fund_freq_prev = fund_freq;
}
