#include "JZsdkLib.h"
#include "version_choose.h"

//main函数参数一是原始pcm文件名,参数二是去噪后的pcm文件名


#ifdef SPEEX_STATUS_ON

#include <stdio.h>
#include <string.h>
#include "speex/speex_echo.h"    // Speex 回声消除头文件
#include "speex/speex_preprocess.h"  // Speex 预处理头文件

/*
graph LR

A[原始语音信号] --> B[预处理]
B --> C[语言检测]
C -->|英语| D[英语参数配置]
C -->|汉语| E[汉语参数配置]
C -->|法语| F[法语参数配置]
D --> G[Speex编码器]
E --> H[Speex编码器]
F --> I[Speex编码器]
G --> J[输出编码数据]
H --> J
I --> J
*/

#define TAIL_TIME 500 //回声尾长,单位毫秒

typedef struct JZ_SpeexInfo{

    //处理的音频样本长度, 一般对应10~20ms的音频数据,太小会增加计算开销,太大会增加处理延迟
    int DealSampleLen; 

    /*
        回声尾长 表示需要消除的回声持续时间(以样本数计) 建议值对应100-500毫秒的音频数据
        对于8kHz采样率:800-4000个样本 
        对于16kHz采样率:1600-8000个样本        
    */
    int TailLen;

    //音频采样率
    int SampleRate;

    // Speex回声消除状态
    SpeexEchoState *EchoState;     

    // Speex预处理状态
    SpeexPreprocessState *PreprocessState;

    // 存储回声消除的样本
    short *EchoBuf;
    int   EchoBufLen;

    //标志位
    int Flag;

}JZ_SpeexInfo;

static JZ_SpeexInfo g_SpeexInfo = {0};

T_JZsdkReturnCode Speex_Deinit()
{        
    if (g_SpeexInfo.Flag == JZ_FLAGCODE_ON)
    {
        speex_echo_state_destroy(g_SpeexInfo.EchoState);      // 释放回声消除状态  
        speex_preprocess_state_destroy(g_SpeexInfo.PreprocessState); // 释放预处理状态

        memset(&(g_SpeexInfo.EchoBuf), 0, sizeof(g_SpeexInfo.EchoBuf));
        g_SpeexInfo.EchoBufLen = 0;

        if (g_SpeexInfo.EchoBuf != NULL)
        {
            free(g_SpeexInfo.EchoBuf);
            g_SpeexInfo.EchoBuf = NULL;
        }

        g_SpeexInfo.Flag = JZ_FLAGCODE_OFF;
    }

    JZSDK_LOG_DEBUG("Speex_Deinit success\n");

    return JZ_ERROR_SYSTEM_MODULE_CODE_SUCCESS;
}

T_JZsdkReturnCode Speex_Init(int SampleRate)
{
    //检查speex的参数
    if (g_SpeexInfo.Flag == JZ_FLAGCODE_ON)
    {
        Speex_Deinit();
    }

    g_SpeexInfo.SampleRate = SampleRate;

    //计算长度
    g_SpeexInfo.TailLen = SampleRate * TAIL_TIME / 1000 ; //可以×1.2作为余量
    g_SpeexInfo.DealSampleLen = 640;   //16000 * time / 1000  //目前是因为程序写死了80 后面可以改
    
    // 初始化回声消除状态
    g_SpeexInfo.EchoState = speex_echo_state_init(g_SpeexInfo.DealSampleLen, g_SpeexInfo.TailLen); 
    g_SpeexInfo.PreprocessState = speex_preprocess_state_init(g_SpeexInfo.DealSampleLen, g_SpeexInfo.SampleRate);  // 初始化预处理状态

    //设置采样率  
    speex_echo_ctl(g_SpeexInfo.EchoState, SPEEX_ECHO_SET_SAMPLING_RATE, &(g_SpeexInfo.SampleRate));

    //将预处理状态与回声消除状态关联
    speex_preprocess_ctl(g_SpeexInfo.PreprocessState, SPEEX_PREPROCESS_SET_ECHO_STATE, g_SpeexInfo.EchoState);

    //注册预处理数据数组
    g_SpeexInfo.EchoBuf = (short *)malloc(g_SpeexInfo.TailLen * sizeof(short));

    g_SpeexInfo.Flag = JZ_FLAGCODE_ON;

    JZSDK_LOG_DEBUG("Speex_Init success\n");

    return JZ_ERROR_SYSTEM_MODULE_CODE_SUCCESS;
}

T_JZsdkReturnCode Speex_DealData(short *InData, short *OutData, int frame_size)
{
    if (g_SpeexInfo.Flag == JZ_FLAGCODE_OFF)
    {
        return JZ_ERROR_SYSTEM_MODULE_CODE_FAILURE;
    }

    //填充回声消除的样本
    if (g_SpeexInfo.EchoBufLen < g_SpeexInfo.TailLen)
    {
        memcpy(&(g_SpeexInfo.EchoBuf[g_SpeexInfo.EchoBufLen]), InData, frame_size * sizeof(short));
        g_SpeexInfo.EchoBufLen += frame_size;
    }
    //如果里面有完整数据
    else if (g_SpeexInfo.EchoBufLen == g_SpeexInfo.DealSampleLen)
    {
        //对数据进行位移
        memmove(g_SpeexInfo.EchoBuf, &(g_SpeexInfo.EchoBuf[frame_size]), (g_SpeexInfo.EchoBufLen - frame_size) * sizeof(short));

        //将新的数据填充到回声消除的样本中
        memcpy(&(g_SpeexInfo.EchoBuf[g_SpeexInfo.EchoBufLen - frame_size]), InData, frame_size * sizeof(short));
    }
    //如果里面的数据超了
    else if (g_SpeexInfo.EchoBufLen > g_SpeexInfo.TailLen)
    {
        return JZ_ERROR_SYSTEM_MODULE_CODE_FAILURE;
    }
    
    //如果没有填充完回声数组
    if (g_SpeexInfo.EchoBufLen < g_SpeexInfo.TailLen)
    {
        return JZ_ERROR_SYSTEM_MODULE_CODE_FAILURE;
    }

    // // 执行回声消除
    // speex_echo_cancellation(g_SpeexInfo.EchoState, InData, g_SpeexInfo.EchoBuf, OutData);

    // // 执行预处理(如噪声抑制等)
    // speex_preprocess_run(g_SpeexInfo.PreprocessState, OutData);

    return JZ_ERROR_SYSTEM_MODULE_CODE_SUCCESS;
}

#endif // SPEEX_STATUS_ON