取消大小周了,周末有了更多的时间来自己学习。给自己立个flag,两周内把fullaec.m里面的线性滤波器、NLP等部分弄懂,发博客;再2~3周的时间看webrtc的AEC3的代码,同样发博客整理;然后再2~3周的时间看一下speex里面的AEC算法。然后再2~3周吃一吃公司的算法,但是这个整理只是自己学习用,不会做任何公开发布。

所以下面开始第一个flag的内容。这周只有空看完了线性部分,仿佛没有看到这里面有DTD的部分,所以先发一下线性部分,后面整理一下非线性部分。在学习过程中,参考了《实时语音处理实践指南》相关内容和网上有关博客的内容,在此对相关作者表示感谢。

下面正文开始。

线性滤波

上图红色虚线框的部分为线性滤波器处理部分。需要解释的有以下几点:

  1. 为什么计算有多少块的时候Nb=floor(NN/N)-M?

答:NN是音频总长度,N是每块的长度。NN/N得到音频有多少块(因为可能为非整数,所以向上取整)。但是因为每次进来都是一块,只有满到底16块了第一个大块才被填满,因此可以看做是第一次进来16块,然后下次进来了一块这才被看做是真正的第一次开始,所以总数里面要减去那刚开始进来的16块,即floor(NN/N)-M。

  1. 为何要将新的64点xk与旧的64点xo组成xx?

答:结合在一起是重叠保留法,将循环卷积转变为线性卷积。两个离散序列的FFT变换结果直接相乘是循环卷积,而我们需要线性卷积。【仍需要进一步理解和google】

  1. 对上图中黑框框住的红色虚线框说明

yfk是对估计的16个块的频谱按列求和,得到的就是最近16块(时间)的远端频谱估计信息,yfk就是最终估计的回声在频域的结果。这样做的好处就是,近端麦克风采集到的信号里只要有这16块(时间)包含的远端信号,就都可以进行消除。这个16块包含了多长时间呢?若采样率fs=16k,则就是16*(64/16000)=0.064s=64ms的数据(每个块是4ms)。也就是说,只要近端采集到的信号里面的回声滞后扬声器64ms以内,就都是可以消除的。

线性滤波器系数更新

对各个上标的解释:
  1. 补0是为了保证能做128点的FFT变换,并且补0并不会影响FFT频率值的结果。
  2. 归一化计算结果实际上是:

Ek2=E(n)/p(n)

  1. 第一步是对Ek2的幅值进行处理得到absEf:若Ek2的幅值大于threshold,则取Ek2的幅值,否则为threshold。将第一步和第二步结合起来,实际上每个点的物理意义就是将Ek2的振幅与门限比较,若比门限小则取该点为1,如果比门限大则该点值为门限值/振幅(因为振幅比门限大,所以值范围为0~1)。由于第二步进行了除以操作,所以第三步实际上是这样一个操作:如果该点的值比门限大,则取门限;如果该点的值比门限小,则保持不变。这样做的好处是降低滤波器系数更新过于快导致滤波器发散的风险。
  2. 实际得到的结果就是(为了方便理解,以时域形式)

mEk=μE(n)/p(n)

  1. 这一步实际上是将65*1的矩阵数据复制出另外15份,得到一个65*16的矩阵,每一列的结果其实是一样的,然后再与远端信号XFm(65*16),第一列是最新的一个块,其他的都是过去15个时间段的块)相乘,实际上得到的就是

X(n)μE(n)/p(n)

  1. 求解IFFT后得到IFPP,将IFPP的后64点置零再变换到频域得到FPH,这样操作是为了避免线性卷积变成循环卷积。

博客内容排版有些混乱,如需访问原版,请点击这里访问,访问密码请关注个人公众号【小奥的学习笔记】,回复fullaec获取。