这个能量指的是短时能量,原理就是近端说话的时候,理论上应该声音能量明显大于只有回声的时候。
把扬声器输出的信号与麦克风采集到的信号做能量比较,采集到的信号如果能量高于输出信号的话,则说明一定有回声。除非麦克风有放大输入信号的作用。由于输入信号和播放信号之间有一定的时延,所以拿当前的输入信号和一段时间之内(取大于等于时延的最小值)的输出信号的最大值做对比,如果输入信号大于这个值,则认为有double talk。 当然精确的说,应该是取正好是从当前时间向前推时延个时间单位的扬声器输出信号和输入做对比。但。。。GEIGEL木有这么做。 例子代码在 https://www.ietf.org/mail-archive/web/avt/current/msg04629.html
int dtdNdx; /* For Normalized Least Means Square - Pre-whitening */ #define NLMS_LEN (240*8) /* maximum NLMS filter length in taps #define DTD_LEN 16 // block size in taps to optimize DTD float max_x[NLMS_LEN/DTD_LEN]; /* Geigel Double-Talk Detector * * in d: microphone sample (PCM as floating point value) * in x: loudspeaker sample (PCM as floating point value) * return: 0 for no talking, 1 for talking */ int dtd(float d, float x); int AEC::dtd(float d, float x) { // optimized implementation of max(|x[0]|, |x[1]|, .., |x[L-1]|): // calculate max of block (DTD_LEN values) x = fabsf(x); if (x > max_x[dtdNdx]) { max_x[dtdNdx] = x; if (x > max_max_x) { max_max_x = x; } } if (++dtdCnt >= DTD_LEN) { dtdCnt = 0; // calculate max of max max_max_x = 0.0f; for (int i = 0; i < NLMS_LEN/DTD_LEN; ++i) { if (max_x[i] > max_max_x) { max_max_x = max_x[i]; } } // rotate Ndx if (++dtdNdx >= NLMS_LEN/DTD_LEN) dtdNdx = 0; max_x[dtdNdx] = 0.0f; } // The Geigel DTD algorithm with Hangover timer Thold if (fabsf(d) >= GeigelThreshold * max_max_x) { hangover = Thold; } if (hangover) --hangover; if (max_max_x < UpdateThreshold) { // avoid update with silence or noise return 1; } else { return (hangover > 0); } }`利用信号相关度,其实就是算余弦相似性,计算扬声器输出信号,麦克风输入信号,回声估计信号之间的相似性计算。若木有dtd时,麦克风输入信号和回声估计信号之间的相似度很大,这样把自适应滤波器的输入回声参考信号和输出回声估计信号的每一帧的采样值做两个矢量,计算他们的夹角。当没有DTD时,该夹角的余弦值基本为1,否则会明显小于1。可以设定一个阈值,小于该阈值时定义为DTD状态。
