首页 > 解决方案 > MATLAB:从 wav 文件中去除高频噪声

问题描述

我正在尝试从以下文件中删除高频噪声。

这是一个女人在看新闻的文件,上面响起一阵高亢的噪音。在文件的末尾,其他人开始说话,但使用的是不同的语言。

我想过滤掉这种尖锐的噪音,并且能够清楚地听到那个女人在看新闻。看频域:

未过滤

我尝试过使用低通滤波器和带阻滤波器。带阻滤波器产生的信号不再具有高音振铃,但音频不是很清晰,很难分辨出在说什么——低通滤波器也是如此。我推测这是因为我不仅过滤掉了噪音,还过滤了语音的谐波。我还需要在过滤后放大音频信号,因为它比以前更安静。

是否有一些聪明的方法可以重建语音的谐波,以便更清楚地听到所说的内容?或者有没有一种聪明的方法可以过滤信号而不会损失太多的音频清晰度?

如果需要,我可以包含我在 matlab 中使用的任何代码。

笔记:

标签: matlabaudiofiltersignal-processingwav

解决方案


鉴于样品中干扰的相当动态的性质,固定过滤器不会产生非常令人满意的结果。为了提高性能,您需要根据干扰的估计动态调整过滤参数。

幸运的是,在这种情况下,干扰非常强,并且呈现出相当规则的模式,这使得它更容易估计。这可以从信号的spectrogram. 对于以下推导,我们将假设 wavfile 的样本已存储在数组x中,并且采样率为fs(在提供的样本中为 8000Hz)。

[Sx,f,t] = spectrogram(x, triang(1024), 1023, [], fs, 'onesided');

在此处输入图像描述

鉴于干扰很强,可以通过定位每个时间片中的峰值频率来获得干扰的频率:

frequency = zeros(size(Sx,2),1);
for k = 1:size(Sx,2)
    [pks,loc] = findpeaks(Sx(:,k));
    frequency(k) = fs * (loc(1)-1);
end

看到干扰是周期性的,我们可以使用离散傅里叶变换来分解这个信号:

M = 32*fs;
Ff = fft(frequency, M);
plot(fs*[0:M-1]/M, 20*log10(abs(Ff));
axis(0, 2);
xlabel('frequency (Hz)');
ylabel('amplitude (dB)');

在此处输入图像描述

使用前两个谐波作为近似值,我们可以将干扰信号的频率建模为:

T = 1.0/fs
t = [0:length(x)-1]*T
freq = 750.0127340203496
       + 249.99913423501602*cos(2*pi*0.25*t - 1.5702946346796276)
       + 250.23974282864816*cos(2*pi*0.5 *t - 1.5701043282285363);

在这一点上,我们将有足够的能力创建一个具有由该频率模型给出的中心频率(随着我们不断更新滤波器系数而动态变化)的窄带滤波器。但是请注意,不断地重新计算和更新滤波器系数是一个相当昂贵的过程,并且鉴于干扰很强,通过锁定干扰相位可以做得更好。这可以通过将原始信号的小块与所需频率的正弦和余弦相关来完成。然后我们可以稍微调整相位以使正弦/余弦与原始信号对齐。

% Compute the phase of the sine/cosine to correlate the signal with
delta_phi = 2*pi*freq/fs;
phi = cumsum(delta_phi);

% We scale the phase adjustments with a triangular window to try to reduce
% phase discontinuities. I've chosen a window of ~200 samples somewhat arbitrarily,
% but it is large enough to cover 8 cycles of the interference around its lowest
% frequency sections (so we can get a better estimate by averaging out other signal
% contributions over multiple interference cycles), and is small enough to capture
% local phase variations.
step = 50;
L    = 200;
win  = triang(L);
win  = win/sum(win);

for i = 0:floor((length(x)-(L-step))/step)
    % The phase tweak to align the sine/cosine isn't linear, so we run a few
    % iterations to let it converge to a phase locked to the original signal
    for iter = 0:1
        xseg   = x[(i*step+1):(i*step+L+1)];
        phiseg = phi[(i*step+1):(i*step+L+1)];
        r1 = sum(xseg .* cos(phiseg));
        r2 = sum(xseg .* sin(phiseg));
        theta = atan2(r2, r1);

        delta_phi[(i*step+1):(i*step+L+1)] = delta_phi[(i*step+1):(i*step+L+1)] - theta*win;
        phi = cumsum(delta_phi);
    end
end

最后,我们需要估计干扰的幅度。在这里,我们选择在初始 0.15 秒内执行估计,在语音开始前有一点停顿,这样估计就不会受到语音幅度的影响:

tmax = 0.15;
nmax = floor(tmax * fs);
amp  = sqrt(2*mean(x[1:nmax].^2));
% this should give us amp ~ 0.250996990794946

然后,这些参数允许我们相当精确地重建干扰,并相应地通过直接减法从原始信号中去除干扰:

y = amp * cos(phi)
x = x-y

在此处输入图像描述

聆听产生的输出,您可能会注意到残留的微弱的嗖嗖声,但与原始干扰相比没有任何意义。显然,这是一个相当理想的情况,其中干扰的参数很容易估计,结果看起来好得令人难以置信。对于更多随机干扰模式,您可能无法获得相同的性能。

注意:用于此处理的 python 脚本(以及相应的 .wav 文件输出)可以在这里找到。


推荐阅读