问题描述:
HISI 3518EV200 平台开发的IPC产品,其中具有对讲功能,能和手机APP端进行全双工双向对讲,但全双工的时候碰到一个问题,就是会产生回音,当人在手机APP端说话时,往往能听到设备端回回来的回音(设备端喇叭播放的声音经过MIC又传回手机端)。
解决方法:
实际情况:
AEC采用默认的参数设置,发现全双工语音对讲仍然有回音,虽然听不
出回音的内容,但能确定是自己说话的回过来的声音。
方法详解(本文都是针对方法2来展开的):
依据HISI开发文档:
与其他功能模块只需要 Sin 数据不同, AEC 模块需要 Sin(Signal In)和 Rin
(Reference In)两路数据来进行算法处理,最终得到处理后的 sou(Signal Out)数据。
其中, Sin 为加入了回声的近端输入, Rin 为参考帧(回声)数据。成功启用回声抵消
需要具备一定条件:单声道模式,工作采样率为 8kHz、 16kHz,且 MIC 采集语音数据
的 AI 帧长和远程语音播放的 AO 配置帧长必须相同。以上条件 AI 和 AO 都必须满
足。
以上标红部分为HISI开发文档注明开启AEC功能需要满足的条件。需自行满足。
所以代码中,AEC 和 ANR 功能都放到AI AO初始化的部分来做了(ANR语音降噪功能在做
AEC功能时顺便就做了ANR)。
typedef struct hiAI_AEC_CONFIG_S
{
HI_BOOL bUsrMode;
HI_S8 s8CngMode; /* cosy-noisy mode:0 close,1 open, default 1*/
HI_S8 s8NearAllPassEnergy;
HI_S8 s8NearCleanSupEnergy;
HI_S16 s16DTHnlSortQTh;
HI_S16 s16EchoBandLow;
HI_S16 s16EchoBandHigh;
HI_S16 s16EchoBandLow2;
HI_S16 s16EchoBandHigh2;
HI_S16 s16ERLBand[6];
HI_S16 s16ERL[7];
HI_S16 s16VioceProtectFreqL;
HI_S16 s16VioceProtectFreqL1;
HI_S32 s32Reserved;
} AI_AEC_CONFIG_S;
关于该数据结构,其中有几点注意的,HISI完档中提到了 :
【注意事项】
【相关数据类型及接口】
AI_VQE_CONFIG_S
在完成AEC功能的添加,采用默认的参数设置后,发现全双工语音对讲仍然有回音(虽然听不出回音的内容,但能确定是自己说话的回过来的声音)。后来经过调整“语音处理频段”的高低频参数范围来达到了消除回音的效果:
stAiVqeAttr.stAecCfg.s16EchoBandLow = 10; //default:10
stAiVqeAttr.stAecCfg.s16EchoBandHigh = 25; //default:41
stAiVqeAttr.stAecCfg.s16EchoBandLow2 = 28; //default:47
stAiVqeAttr.stAecCfg.s16EchoBandHigh2 = 35; //default:63
附上代码:
AI部分:
/*****************************************************************************
函数名称:StartAudioAI
函数功能:开启输入,单通道chid --1
输入参数:无
输出参数:无
返 回 值:无
使用说明: 内部调用
******************************************************************************/
int Audio::StartAudioAI()
{
HI_S32 s32Ret;
AUDIO_DEV AiDevId = 0;
AI_CHN AiChn = 0;
s32Ret = HI_MPI_AI_SetPubAttr(AiDevId, &m_stAioAttr);
if (s32Ret)
{
printf("%s: HI_MPI_AI_SetPubAttr(%d) failed with %#x\n", __FUNCTION__, AiDevId, s32Ret);
return HI_FAILURE;
}
if (HI_MPI_AI_Enable(AiDevId))
{
printf("%s: HI_MPI_AI_Enable(%d) failed with %#x\n", __FUNCTION__, AiDevId, s32Ret);
return HI_FAILURE;
}
if (HI_MPI_AI_EnableChn(AiDevId, AiChn))
{
printf("%s: HI_MPI_AI_EnableChn(%d,%d) failed with %#x\n", __FUNCTION__,\
AiDevId, AiChn, s32Ret);
return HI_FAILURE;
}
#if 1
HI_S16 s16ERLBand[6] = {4,6,36,49,50,51};
HI_S16 s16ERL[7] = {7,10,16,10,18,18,18};
#define SAMPLE_AUDIO_PTNUMPERFRM 160
AI_VQE_CONFIG_S stAiVqeAttr;
stAiVqeAttr.s32WorkSampleRate = AUDIO_SAMPLE_RATE_8000;
stAiVqeAttr.s32FrameSample = SAMPLE_AUDIO_PTNUMPERFRM;
stAiVqeAttr.enWorkstate = VQE_WORKSTATE_COMMON;
//AEC回音消除
printf("********init AEC******* \n");
stAiVqeAttr.bAecOpen = HI_TRUE;
stAiVqeAttr.stAecCfg.bUsrMode = HI_TRUE;
stAiVqeAttr.stAecCfg.s8CngMode = 0;
#if 1
stAiVqeAttr.stAecCfg.s8NearAllPassEnergy = 1; //default:1
stAiVqeAttr.stAecCfg.s8NearCleanSupEnergy = 2; //default:2
stAiVqeAttr.stAecCfg.s16DTHnlSortQTh = 16384; //default:16384
stAiVqeAttr.stAecCfg.s16EchoBandLow = 10; //default:10
stAiVqeAttr.stAecCfg.s16EchoBandHigh = 25; //default:41
stAiVqeAttr.stAecCfg.s16EchoBandLow2 = 28; //default:47
stAiVqeAttr.stAecCfg.s16EchoBandHigh2 = 35; //default:63
memcpy(&stAiVqeAttr.stAecCfg.s16ERLBand,&s16ERLBand,sizeof(s16ERLBand));//default
memcpy(&stAiVqeAttr.stAecCfg.s16ERL,&s16ERL,sizeof(s16ERL));//default
stAiVqeAttr.stAecCfg.s16VioceProtectFreqL = 3; //default
stAiVqeAttr.stAecCfg.s16VioceProtectFreqL1 = 6; //default
#endif
stAiVqeAttr.bAgcOpen = HI_TRUE;
stAiVqeAttr.stAgcCfg.bUsrMode = HI_FALSE;
//ANR语音降噪
stAiVqeAttr.bAnrOpen = HI_TRUE;
#if 1
stAiVqeAttr.stAnrCfg.bUsrMode = HI_TRUE;
stAiVqeAttr.stAnrCfg.s16NrIntensity = 25; //降噪力度配置,取值为[0, 25]
stAiVqeAttr.stAnrCfg.s16NoiseDbThr = 60; //噪声门限配置,取值为[30,60],配置值越大,检测力度越弱,声音更平滑
stAiVqeAttr.stAnrCfg.s8SpProSwitch = 0; //音乐检测开关,取值为[0,1]
#endif
stAiVqeAttr.bHpfOpen = HI_TRUE;
stAiVqeAttr.stHpfCfg.bUsrMode = HI_TRUE;
stAiVqeAttr.stHpfCfg.enHpfFreq = AUDIO_HPF_FREQ_150;
stAiVqeAttr.bRnrOpen = HI_FALSE;
stAiVqeAttr.bEqOpen = HI_FALSE;
stAiVqeAttr.bHdrOpen = HI_FALSE;
#define SAMPLE_AUDIO_AO_DEV 0
AO_CHN AoChn = 0;
s32Ret = HI_MPI_AI_SetVqeAttr(AiDevId, AiChn, SAMPLE_AUDIO_AO_DEV, AoChn, &stAiVqeAttr);
if (s32Ret)
{
printf("%s: HI_MPI_AI_SetVqeAttr(%d) failed with %#x\n", __FUNCTION__, AiDevId, s32Ret);
return HI_FAILURE;
}
s32Ret = HI_MPI_AI_EnableVqe(AiDevId, AiChn);
if (s32Ret)
{
printf("%s: HI_MPI_AI_EnableVqe(%d,%d) failed with %#x\n", __FUNCTION__, AiDevId, AiChn, s32Ret);
return s32Ret;
}
#endif
HI_MPI_AI_EnableChn(AiDevId, 1);
return HI_SUCCESS;
}
AO部分:
/*****************************************************************************
函数名称:StartAudioAO
函数功能:开启输出,双声道
输入参数:无
输出参数:无
返 回 值:无
使用说明: 内部调用
******************************************************************************/
int Audio::StartAudioAO()
{
HI_S32 s32Ret;
AUDIO_DEV AoDevId =0;
AO_CHN AoChn=0;
ADEC_CHN AdChn=0 ;
AIO_ATTR_S AioAttr;
memset(&AioAttr,0x0,sizeof(AIO_ATTR_S));
memcpy(&AioAttr,&m_stAioAttr,sizeof(AIO_ATTR_S));
AioAttr.u32PtNumPerFrm = NumPerFrm;
AioAttr.enBitwidth = AUDIO_BIT_WIDTH_16;
// AIO_ATTR_S *pstAioAttr = &AioAttr;
// AIO_ATTR_S *pstAioAttr = &m_stAioAttr;
s32Ret = HI_MPI_AO_SetPubAttr(AoDevId, &AioAttr);
if(HI_SUCCESS != s32Ret)
{
printf("%s: HI_MPI_AO_SetPubAttr(%d) failed with %#x!\n", __FUNCTION__, \
AoDevId,s32Ret);
return HI_FAILURE;
}
s32Ret = HI_MPI_AO_Enable(AoDevId);
if(HI_SUCCESS != s32Ret)
{
printf("%s: HI_MPI_AO_Enable(%d) failed with %#x!\n", __FUNCTION__, \
AoDevId, s32Ret);
return HI_FAILURE;
}
MPP_CHN_S stSrcChn,stDestChn;
AdChn = 0;
for(unsigned int i=0;i< 1;i++)
{
AoChn = i;
stSrcChn.enModId = HI_ID_ADEC;
stSrcChn.s32DevId = 0;
stSrcChn.s32ChnId = AdChn;
stDestChn.enModId = HI_ID_AO;
stDestChn.s32DevId = AoDevId;
stDestChn.s32ChnId = AoChn;
s32Ret = HI_MPI_AO_EnableChn(AoDevId, AoChn);
if(HI_SUCCESS != s32Ret)
{
printf("%s: HI_MPI_AO_EnableChn(%d) failed with %#x!\n", __FUNCTION__,\
AoChn, s32Ret);
return HI_FAILURE;
}
#if 1
#define SAMPLE_AUDIO_PTNUMPERFRM 160
AO_VQE_CONFIG_S stAoVqeAttr;
memset(&stAoVqeAttr, 0, sizeof(AO_VQE_CONFIG_S));
stAoVqeAttr.bEqOpen = HI_TRUE;
stAoVqeAttr.s32WorkSampleRate = AUDIO_SAMPLE_RATE_8000;
stAoVqeAttr.s32FrameSample = SAMPLE_AUDIO_PTNUMPERFRM;
stAoVqeAttr.enWorkstate = VQE_WORKSTATE_COMMON;
stAoVqeAttr.bAgcOpen = HI_TRUE;
stAoVqeAttr.stAgcCfg.bUsrMode = HI_FALSE;
stAoVqeAttr.bAnrOpen = HI_TRUE;
#if 1
stAiVqeAttr.stAnrCfg.bUsrMode = HI_TRUE;
stAiVqeAttr.stAnrCfg.s16NrIntensity = 25;//降噪力度配置,取值为[0, 25]
stAiVqeAttr.stAnrCfg.s16NoiseDbThr = 60;//噪声门限配置,取值为[30,60],配置值越大,检测力度越弱,声音更平滑
stAiVqeAttr.stAnrCfg.s8SpProSwitch = 0;//音乐检测开关,取值为[0,1]
#endif
stAoVqeAttr.bHpfOpen = HI_TRUE;
stAoVqeAttr.stHpfCfg.bUsrMode = HI_TRUE;
stAoVqeAttr.stHpfCfg.enHpfFreq = AUDIO_HPF_FREQ_150;
stAoVqeAttr.bEqOpen = HI_FALSE;
s32Ret = HI_MPI_AO_SetVqeAttr(AoDevId, i, &stAoVqeAttr);
if(HI_SUCCESS != s32Ret)
{
printf("line %d: HI_MPI_AO_SetVqeAttr(%d) failed with %#x!\n", __LINE__,\
AoChn, s32Ret);
return HI_FAILURE;
}
s32Ret = HI_MPI_AO_EnableVqe(AoDevId, i);
if (s32Ret)
{
printf("%s: HI_MPI_AO_EnableVqe(%d,%d) failed with %#x\n", __FUNCTION__, AoDevId, i, s32Ret);
return s32Ret;
}
#endif
s32Ret = HI_MPI_SYS_Bind(&stSrcChn, &stDestChn);
if(HI_SUCCESS != s32Ret)
{
printf("line %d: HI_MPI_SYS_Bind(%d) failed with %#x!\n", __LINE__,\
AoChn, s32Ret);
return HI_FAILURE;
}
}
return HI_SUCCESS;
}
实际调试效果还得以实际设备为准,不同设备不同的电路,会有不一样的噪声干扰等,会导致参数不一样。