一、概述
自适应滤波器做啸叫抑制,类似回声消除的方法,刚开始受一些调研资料的影响,由于信号的相关性,自适应滤波器会完全消除语音。后来随着研究的深入,语音帧之间的相关性没有想象中的强烈,是当前主流的方法。直接使用NLMS算法在时域就可以取得较好的效果。后面进行了工程化,使用频域分块自适应滤波器,取得了理想的效果。关于自适应滤波器进行啸叫抑制,参考数据必须是扬声器播放之前的数据,也就是自动增益控制算法之后的数据。
二、算法仿真
2.1 算法流程图
2.2 时域python仿真
#!/usr/bin/python
from __future__ import division
# importimport numpy as np
import matplotlib.pyplot as plt
import soundfile as sfdef simulate_howling_env():input_file = "test/LDC93S6A.wav"howling_file = "test/remove_howling.wav"# load clean speech filex, Srate = sf.read(input_file)# pre design a room impulse responserir = np.loadtxt('test/path.txt', delimiter='\t')# G : gain from mic to speakerG = 0.2# ====== set parameters ========interval = 0.02 # frame interval = 0.02sSlen = int(np.floor(interval * Srate))if Slen % 2 == 1:Slen = Slen + 1PERC = 50 # window overlap in percent of frame sizelen1 = int(np.floor(Slen * PERC / 100))len2 = int(Slen - len1)nFFT = 2 * SlenN = min(2000, len(rir)) # limit room impulse response lengthx2 = np.zeros(N) # buffer N samples of speaker output to generate acoustic feedbacky = np.zeros(len(x)) # save speaker output to yy1 = 0.0 # init as 0w = np.zeros(N)u = np.zeros(N)mu = 0.01for i in range(len(x)):x1 = x[i] + y1e = x1 - np.dot(u, w)w = w + mu * e * u / (np.dot(u, u) + 1e-6)x1 = ey[i] = G * x1y[i] = min(2, y[i]) # amplitude clippingy[i] = max(-2, y[i])x2[1:] = x2[:N - 1]x2[0] = y[i]u[1:] = u[:N - 1]u[0] = y[i]y1 = np.dot(x2, rir[:N])sf.write(howling_file, y, Srate)plt.figure()plt.subplot(2, 1, 1)plt.plot(y)plt.xlim((0, len(y)))plt.subplot(2, 1, 2)plt.specgram(y, NFFT=nFFT, Fs=Srate, noverlap=len2, cmap='jet')plt.ylim((0, 5000))plt.ylabel("Frquency (Hz)")plt.xlabel("Time (s)")plt.show()simulate_howling_env()
2.3 时域c仿真
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>#define OUTPUT_PATH_NAME "/tmp/node1_to_node2.tmp"
#define INPUT_PATH_NAME "/tmp/node2_to_node1.tmp"int main() {if (access(INPUT_PATH_NAME, F_OK) != -1) {printf("File exists.\n");} else {mkfifo(INPUT_PATH_NAME, 0666);}int fin = open(INPUT_PATH_NAME, O_RDONLY);int fout = open(OUTPUT_PATH_NAME, O_WRONLY);int frames = 128;int size = frames * 2;char *buffer = (char *) malloc(size);int chunk_num = 5;float mu = 0.01;float *u = (float*) malloc(sizeof(float) * frames * chunk_num);float *w = (float*) malloc(sizeof(float) * frames * chunk_num);for (int i = 0; i < frames * chunk_num; i++) {u[i] = 0;w[i] = 0;}float *current_out_data = (float*) malloc(sizeof(float) * frames);float *current_data = (float*) malloc(sizeof(float) * frames);float *last_data = (float*) malloc(sizeof(float) * frames);short *out_data = (short*) malloc(sizeof(short) * frames);for (int i = 0; i < frames; i++) {current_out_data[i] = 0;current_data[i] = 0;last_data[i] = 0;out_data[i] = 0;}while (1) {int rc = read(fin, buffer, size);if (rc <= 0) {fprintf(stderr, "end of file on input/n");break;} else if (rc != size) {fprintf(stderr,"short read: read %d bytes/n", rc);} else {printf("read len: %d\n", rc);}short *input_data = (short*)buffer;//printf("current_data:");for (int i = 0; i < frames; i++) {current_data[i] = (float)(input_data[i]) / 32768.0;//printf(" %f", current_data[i]);}//printf("\n");//printf("current_out_data:");for (int i = 0; i < frames; i++) {memmove(u+1, u, sizeof(float)*(frames*chunk_num-1));u[0] = last_data[i];float uw = 0;float uu = 1e-6;for (int k = 0; k < frames*chunk_num; k++) {uw += u[k]*w[k];uu += u[k]*u[k];}current_out_data[i] = current_data[i] - uw;//printf(" (%f %f %f)", current_out_data[i], uw, uu);for (int k = 0; k < frames*chunk_num; k++) {w[k] = w[k] + mu * current_out_data[i] * u[k] / uu;}}//printf("\n");//printf("out_data:");for (int i = 0; i < frames; i++) {if (current_out_data[i] > 1.0) {current_out_data[i] = 1.0;} else if (current_out_data[i] < -1.0) {current_out_data[i] = -1.0;}out_data[i] = (short)((current_out_data[i]) * 32767.0);//printf(" %d", out_data[i]);}//printf("\n");for (int i = 0; i < frames; i++) {last_data[i] = current_data[i];}rc = write(fout, (char*)out_data, size);if (rc <= 0) {break;} else {printf("write len: %d\n", rc);}}close(fin);close(fout);free(buffer);free(u);free(w);free(current_out_data);free(current_data);free(last_data);free(out_data);unlink(INPUT_PATH_NAME);return 0;
}
2.4 仿真结果
三、总结
本节我们使用自适应滤波法在时域进行去啸叫仿真,下节我们进行频域的实现,完成整个系统的搭建。