【问题标题】:Acoustic Echo Cancellation (AEC) in embedded software嵌入式软件中的回声消除 (AEC)
【发布时间】:2012-07-05 10:43:05
【问题描述】:

我正在嵌入式设备上做一个 VoIP 项目。我使用带有低级音频编解码器的 32 位 MCU 构建了一个示例。现在我发现我的设备上存在回声问题,即我可以从扬声器中听到我说的话。我做了一些研究,发现大多数应用程序使用具有声学回声消除功能的 DSP 编解码器。但是,是否可以使用我的 32 位 MCU 在软件中进行声学回声消除?

您能否介绍用于消除回声的算法,甚至源代码:P?我知道在 MCU 上不可能使用复杂的方法,但也欢迎使用简单的算法。

谢谢

[跟进] : 我尝试了一些 AEC 代码,但它们在我的 MCU 中无法正常工作,可能是 MCU 功率的限制。我发现我的设备在实施这些代码时变得非实时(但 VoIP 需要实时响应)。最后我通过添加一个 AEC 芯片实现了一个模拟硬件解决方案,因为我不想在另一个 DSP 芯片中再次编写代码。

【问题讨论】:

  • 您可以在软件中进行 EC,这几乎就是现在的做法,因为模拟解决方案不能很好地工作(如果有的话)。但这不是一件容易的事。首先,您的硬件必须具有足够的 CPU 能力和内存,并且具有相当线性的模拟部分。高非线性失真通常会使 EC 无法实现。
  • 我也认为这是dsp.stackexchange.com的一个更好的问题。

标签: embedded signal-processing acoustics


【解决方案1】:

如果您正在做一个商业项目,这应该很容易。您可以在 VoIP 应用程序中集成商业音频消除软件。

【讨论】:

    【解决方案2】:

    我在回声消除方面玩得很开心。我写了一个软件电话,用户可以根据自己的喜好来切换他们的音频输入和输出设备。我尝试了 Speex 回声消除库,以及我在网上找到的其他几个开源库。没有一个对我有用。我尝试了不同的扬声器/麦克风配置,但回声总是以某种形式或方式存在。

    我相信创建适用于所有可能的扬声器配置/房间大小/背景噪音等的 AEC 代码将非常困难。最后我坐下来用这个算法为我的软电话编写了自己的回声消除模块。

    它有些粗糙,但运行良好且可靠。

    变量1: 记录与您交谈的人说话时的平均幅度。 (不要考虑安静时间)

    变量2: 记录输入(麦克风)的平均振幅,但仅在有声音时 - 再次 - 不要考虑安静时间。

    只要有音频要播放 - 就切断麦克风。并且假设听的人没有说话,在播放最后一个可听音频帧后 150-300 毫秒打开麦克风。

    如果来自麦克风的音频(您在播放期间丢弃的)大于 oh-say (variable2 * 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 扬声器......我对此很满意。

    我希望它对你有所帮助,或者给你一些东西……

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-02-18
      • 1970-01-01
      • 2013-09-23
      相关资源
      最近更新 更多