
#include "app_codec.h"
#include "app_algorithm.h"
#include "app_buffer.h"
#include "board_config.h"
#include "libWnvc320Decoder.h"

#include "asr.h"
#include "asr_config.h"

#ifdef CONFIG_XIP
#define ATTRIBUTE_DATA __attribute__((section(".ram.code")))
#else
#define ATTRIBUTE_DATA
#endif

#define ASR_ALGO

volatile unsigned int asr_tick_timer;	   //用于计时识别时间，每1ms加1
volatile unsigned char f_asr_response = 0; //识别词条标志， 0~0xff
unsigned char f_asr_wakeup = 0;			   //识别唤醒标志

volatile unsigned int asr_wakeup_tick_timer; //唤醒计时，每1ms加1

//除了备注了的，其他不要动：音频处理的
static unsigned int app_queue_temp_buf[CODEC_RX_FRAME_BYTE_SIZE / 4];
static unsigned int app_hp_output_new_flag = 0;
static unsigned int app_mic_input_cnt = 0;
static unsigned int app_hp_output_cnt = 0;
static unsigned int app_hp_output_error_cnt = 0;
static unsigned int app_hp_output_ok_cnt = 0;

#define ASR_FRAME_SAMPLE_PER_WORD_SIZE 480 //30ms ,@16k 16bits,mono
#define ASR_FRAME_SAMPLE_NUM 3
#define ASR_FRAME_SAMPLE_FULL_WORD_SIZE ASR_FRAME_SAMPLE_PER_WORD_SIZE *ASR_FRAME_SAMPLE_NUM

typedef struct asrbuffer
{
	volatile unsigned short *buffer;
	unsigned int write;
	unsigned int read;
	unsigned int new_data_len;
} asrbuffer_t;

static volatile asrbuffer_t asr_pcm_buf;
static volatile unsigned short app_algorithm_asr_pcm_buf[ASR_FRAME_SAMPLE_FULL_WORD_SIZE];

unsigned short Beep_state_count = 0;  //音频解码下标，每次递增44
static int WnvcDecInlen;			  //解码前音频输入长度
static int WnvcDecOutlen;			  //解码后音频输出长度
static short Wvc320DecOut[320 * 2];	  //解码后音频输出
static unsigned char Wvc320DecIn[44]; //解码前音频输入

// 算法上电时初始化，不要动，识别初始化
void app_algorithm_power_on_init(void)
{
#ifdef ASR_ALGO
	if (Wanson_ASR_Init() < 0)
	{
#if APP_LOG_ENABLE
		printf("Wanson_ASR_Init Failed!\n");
#endif
		return;
	}

	Wanson_ASR_Reset();
#endif
	asr_pcm_buf.buffer = app_algorithm_asr_pcm_buf;
	asr_pcm_buf.write = 0;
	asr_pcm_buf.read = 0;
	asr_pcm_buf.new_data_len = 0;

	Wnvc320DecoderInit();
}
/* 提示音播放解码函数，二次开发尽量不要动 */
void app_response_decode_handle(void)
{
	//static unsigned char f_old_asr;
	unsigned int i, j;
	if (f_asr_response)
	{

		gpio2_set(1); //指示灯，播放的时候闪，没啥需求的可以删掉
		app_hp_output_new_flag = 0;
		//f_old_asr = f_asr_response;
		if (f_asr_response <= WNVC_BUFF_TAB_NUM)
		{							   //有匹配的提示音
			asr_wakeup_tick_timer = 0; //每一次唤醒计时在提示音播放完开始，
									   //在这里放一个主要是为了避免播放提示音的会后退出唤醒了；
									   //如果不修改退出唤醒的地方的话，这里可以删除
			app_codec_dac_power_control(true);
			WnvcDecInlen = 44;
			for (j = 0; j < WnvcDecInlen; j++)
			{
				Wvc320DecIn[j] = wnvc_buffer_tab[f_asr_response - 1].buffer[Beep_state_count + j];
			}
			Wnvc320Decoder(Wvc320DecIn, WnvcDecInlen, Wvc320DecOut, &WnvcDecOutlen); //提示音解码，一帧20ms 320sample
			Beep_state_count += 44;
			i = 320;
			while (i--)
			{ //单声道转双声道
				Wvc320DecOut[2 * i + 1] = Wvc320DecOut[i];
				Wvc320DecOut[2 * i] = Wvc320DecOut[i];
			}
			DAcHpOutQueueWrite((unsigned int *)Wvc320DecOut, 320); //填入HP OUT队列
			DAcHpOutQueueFullAdjust();

			if (Beep_state_count >= wnvc_buffer_tab[f_asr_response - 1].length)
			{ //提示音播放完毕
				Beep_state_count = 0;
				f_asr_response = 0;
				asr_wakeup_tick_timer = 0; //每一次唤醒计时在提示音播放完开始
#if APP_LOG_ENABLE
				printf("\nFinished playing,Ready for the next one.\n");
#endif
				app_codec_dac_power_control(false);
			}
		}
		else
		{ //没有匹配的提示音
			f_asr_response = 0;
			app_codec_dac_power_control(false);
		}
		gpio2_set(0); //指示灯，播放的时候闪，没啥需求的可以删掉
	}
}

/* 不要动 */
ATTRIBUTE_DATA void app_user_timer_thread(void)
{ //定时中断回调里处理codec input/output buffer，保证音频数据流
	if (app_codec_adc_input((unsigned int *)app_queue_temp_buf, CODEC_RX_FRAME_BYTE_SIZE))
	{
		app_mic_input_cnt++;

		unsigned short *pdata, i;
		// 		 if(asr_pcm_buf.new_data_len >= ASR_FRAME_SAMPLE_FULL_WORD_SIZE)//asr buffer 溢出
		// 		 {//asr算法处理不够及时，直接透传codec input到output
		//		  	pdata = (unsigned short *)app_queue_temp_buf;
		//		 	for (i = 0; i < CODEC_RX_FRAME_BYTE_SIZE/4; i++)
		//		 	{
		//		 		*(pdata + 1) = *(pdata);//原数据左声道覆盖右声道
		//		 		pdata += 2;
		//		 	}
		// 		 	DAcHpOutQueueWrite((unsigned int *)app_queue_temp_buf, CODEC_RX_FRAME_BYTE_SIZE/4);//填入HP OUT队列
		// 		 	DAcHpOutQueueFullAdjust();
		// 		}
		// 		else
		{
			pdata = (unsigned short *)app_queue_temp_buf;
			for (i = 0; i < CODEC_RX_FRAME_BYTE_SIZE / 4; i++)
			{ //取单声道
#if 1
				*(asr_pcm_buf.buffer + asr_pcm_buf.write + i) = *(pdata); //只取左声道，存到asr队列里
				*(pdata + 1) = *(pdata);								  //原数据左声道覆盖右声道
#else
				*(asr_pcm_buf.buffer + asr_pcm_buf.write + i) = *(pdata + 1); //只取右声道，存到asr队列里
				*(pdata) = *(pdata + 1);									  //原数据右声道覆盖左声道
#endif
				pdata += 2;
			}
#if 0
			 if(f_asr_response == 0) //提示音是输出右声道---单端输出 HPOUT_R_P
			 {//无提示音播放时，bypass录音
			 	DAcHpOutQueueWrite((unsigned int *)app_queue_temp_buf, CODEC_RX_FRAME_BYTE_SIZE/4);//填入HP OUT队列
			 	DAcHpOutQueueFullAdjust();
			 }
#endif
			asr_pcm_buf.write += CODEC_RX_FRAME_BYTE_SIZE / 4;
			asr_pcm_buf.write %= ASR_FRAME_SAMPLE_FULL_WORD_SIZE;
			asr_pcm_buf.new_data_len += CODEC_RX_FRAME_BYTE_SIZE / 4;
		}
	}

	if (cb_codec_output_transfer_flag)
	{ //Codec i2s output irq之后填入新数据
		cb_codec_output_transfer_flag = 0;
		app_hp_output_new_flag = 1;
		DAcHpOutQueueEmptyAdjust();
		unsigned int t_len = DAcHpOutQueueRead((unsigned int *)app_queue_temp_buf, CODEC_TX_FRAME_BYTE_SIZE / 4); //读出HP OUT队列
		if (t_len != CODEC_TX_FRAME_BYTE_SIZE / 4)
		{
			app_hp_output_error_cnt++;
		}
		app_hp_output_cnt++;
		if (app_codec_dac_output((unsigned int *)app_queue_temp_buf, CODEC_TX_FRAME_BYTE_SIZE))
		{
			app_hp_output_ok_cnt++;
		}
	}
}

/* 唤醒状态计时是否超时，超时返回1 */
unsigned short asr_is_timeout(unsigned int timeOut)
{
	return asr_wakeup_tick_timer > timeOut ? 1 : 0;
}

void app_algorithm_process(void)
{
	int ret = 0;
	const char *text;
	float score;
	//unsigned short i;

	if (asr_pcm_buf.new_data_len >= ASR_FRAME_SAMPLE_PER_WORD_SIZE) //480/16 = 30ms
	{
		if (f_asr_response == 0) //上一条识别结果播放完成时，处理新MIC数据，即播放提示音的时候是不能识别的
		{
			asr_tick_timer = 0;
#ifdef ASR_ALGO
			gpio0_set(1); //指示灯，没啥用，可以删除
			ret = Wanson_ASR_Recog((short *)((asr_pcm_buf.buffer) + (asr_pcm_buf.read)), ASR_FRAME_SAMPLE_PER_WORD_SIZE, &text, &score);
			gpio0_set(0); //指示灯，没啥用，可以删除
#if APP_LOG_ENABLE
			//printf("asr cost: %d ms\r\n", asr_tick_timer); //处理时间打印
			//printf("asr cost: %d ms  %d %d\r\n", asr_tick_timer, reg_cpfmtr, reg_cpfatr);//处理时间打印
#endif
			if (ret == 1)
			{ //有识别结果
				asr_deal(text);
			}
#endif
			if (f_asr_wakeup && asr_is_timeout(VOICE_WAKE_TIMES*1000))
			{							   //退出唤醒
				f_asr_wakeup = 0;		   //退出唤醒后将唤醒状态标志位清零
				asr_wakeup_tick_timer = 0; //退出唤醒后将唤醒时间计时清零
				gpio1_set(0);			   //指示灯，没啥用，可以删除，唤醒亮，退出唤醒灭
				wake_down_play();
			}
		}
		asr_sync_master_data();
		asr_pcm_buf.read += ASR_FRAME_SAMPLE_PER_WORD_SIZE;			//480
		asr_pcm_buf.read %= ASR_FRAME_SAMPLE_FULL_WORD_SIZE;		//480
		asr_pcm_buf.new_data_len -= ASR_FRAME_SAMPLE_PER_WORD_SIZE; //480
	}

	if (f_asr_response && app_hp_output_new_flag)
	{
		app_response_decode_handle(); //Codec i2s output一帧后，提示音解码一帧填入HP OUT队列
	}
}