我在嵌入式设备上做一个VoIP项目。我使用具有低级音频编解码器的32位MCU构建了一个样本。现在我发现我的设备上有回声问题,就是我可以听到我从发言者那里说的话。我做了一些研究,发现大多数应用都使用具有声学回声消除功能的DSP编解码器。但是,我可以使用32位MCU在软件中进行声学回声消除吗?
您可以使用算法,甚至是源代码:P,用于进行声学回声消除吗?我知道在MCU上不可能使用复杂的方法,而且也欢迎使用简单的算法。
谢谢
[跟进]:我尝试了一些AEC代码,但它们在我的MCU中无法正常工作,可能是MCU功率的限制。我发现在实现这些代码时我的设备变得非实时(但VoIP需要实时响应)。最后,我通过添加AEC芯片实现了模拟硬件解决方案,因为我不想再在另一个DSP芯片中编写代码。
答案 0 :(得分:3)
我有一个回声消除的时间。我写了一个软电话,用户可以切换他们的音频输入和输出设备以适应他们的想象。我尝试了Speex回声消除库,以及我在网上找到的其他几个开源库。没有一个对我很好。我尝试了不同的扬声器/麦克风配置,并且回声总是以某种形式或方式存在。
我认为创建适用于所有可能的扬声器配置/房间大小/背景噪音的AEC代码将非常困难。等等。最后,我坐下来用这个算法为我的软电话写了我自己的回声消除模块。
虽然有些粗糙,但它运作良好且可靠。
变量1: 记录您正在讲话的人说话时的平均幅度。 (不要考虑安静时间)
变量: 记录输入(麦克风)的平均幅度,但只有在有声音的情况下 - 再次 - 不要考虑安静时间。
只要有音频播放 - 切割麦克风。并且假设听的人没有说话,在最后一个可听音频帧进入后播放麦克风150-300ms。
如果来自麦克风的音频(您在播放期间丢失的音频)大于oh- say(变量2 * 1.5),则开始发送音频输入帧达指定的持续时间,每次输入幅度到达时重置该持续时间(变量2 * 1.5)。
这样说话的人会知道他们被打断了,并且停下来看看这个人说的是什么。如果说话的人没有太多背景噪音,他们可能会听到大部分(如果不是全部)中断。
就像我说的那样,不是最优雅的,但它并没有使用大量资源(CPU,内存),而且它实际上非常好用。我很高兴看到我的声音。
为了实现它,我只做了一些功能。
在收到的音频帧上,我调用了一个我调用的函数:
void audioin( AEC *ec, short *frame ) {
unsigned int tas=0; /* Total sum of all audio in frame (absolute value) */
int i=0;
for (;i<160;i++)
tas+=ABS(frame[i]);
tas/=160; /* 320 byte frames muLaw */
if (tas>300) { /* I assume this is audiable */
lockecho(ec);
ec->lastaudibleframe=GetTickCount64();
unlockecho(ec);
}
return;
}
在发送帧之前,我这样做:
#define ECHO_THRESHOLD 300 /* Time to keep suppression alive after last audible frame */
#define ONE_MINUTE 3000 /* 3000 20ms samples */
#define AVG_PERIOD 250 /* 250 20ms samples */
#define ABS(x) (x>0?x:-x)
char removeecho( AEC *ec, short *aecinput ) {
int tas=0; /* Average absolute amplitude in this signal */
int i=0;
unsigned long long *tot=0;
unsigned int *ctr=0;
unsigned short *avg=0;
char suppressframe=0;
lockecho(ec);
if (ec->lastaudibleframe+ECHO_THRESHOLD > GetTickCount64() ) {
/* If we're still within the threshold for echo (speaker state is ON) */
tot=&ec->t_aiws;
ctr=&ec->c_aiws;
avg=&ec->aiws;
} else {
/* If we're outside the threshold for echo (speaker state is OFF) */
tot=&ec->t_aiwos;
ctr=&ec->c_aiwos;
avg=&ec->aiwos;
}
for (;i<160;i++) {
tas+=ABS(aecinput[i]);
}
tas/=160;
if (tas>200) {
(*tot)+=tas;
(*avg)=(unsigned short)((*tot)/( (*ctr)?(*ctr):1));
(*ctr)++;
if ((*ctr)>AVG_PERIOD) {
(*tot)=(*avg);
(*ctr)=0;
}
}
if ( (avg==&ec->aiws) ) {
tas-=ec->aiwos;
if (tas<0) {
tas=0;
}
if ( ((unsigned short) tas > (ec->aiws*1.5)) && ((unsigned short)tas>=ec->aiwos) && (ec->aiwos!=0) ) {
suppressframe=0;
} else {
suppressframe=1;
}
}
if (suppressframe) { /* Silence frame */
memset(aecinput, 0, 320);
}
unlockecho(ec);
return suppressframe;
}
如果需要,将使框架静音。我保留了所有变量,比如定时器和AEC结构中的幅度平均值,我从调用中返回
AEC *initecho( void ) {
AEC *ec=0;
ec=(AEC *)malloc(sizeof(AEC));
memset(ec, 0, sizeof(AEC));
ec->aiws=200; /* Just a default guess as to what the average amplitude would be */
return ec;
}
typedef struct aec {
unsigned long long lastaudibleframe; /* time stamp of last audible frame */
unsigned short aiws; /* Average mike input when speaker is playing */
unsigned short aiwos; /*Average mike input when speaker ISNT playing */
unsigned long long t_aiws, t_aiwos; /* Internal running total (sum of PCM) */
unsigned int c_aiws, c_aiwos; /* Internal counters for number of frames for averaging */
unsigned long lockthreadid; /* Thread ID with lock */
int stlc; /* Same thread lock-count */
} AEC;
你可以随心所欲地适应这个想法,但就像我说的那样。它实际上听起来很不错。我唯一的问题是它们是否有很多背景噪音。但对我来说,如果他们拿起他们的USB手机或使用耳机,他们可以关闭回声消除,而不用担心它......但是虽然带有麦克风的PC扬声器......我很高兴。
我希望它有所帮助,或者给你一些建立......
答案 1 :(得分:0)
如果您正在做一个商业项目,这应该很容易。您可以在VoIP应用程序中集成商业音频取消软件。