原理
收到的带有回声的信号:
其中g(t)是带有回声的音频信号, f(t)是没有回声的原音频信号,αf(t-∆)是带有损耗的回声信号,其中α是衰减系数,∆为回声信号的时延。
对其进行相关运算:
R_gg (τ)=E{g(t)g(t+τ)}=E{(f(t)+)(f(t+τ)+αf(t+τ-∆))}
=E{(f(t)f(t+τ)}+E{α^2 f(t-∆)f(t+τ-∆)}+E{αf(t)f(t+τ-∆)}+E{αf(t+τ)f(t-∆)}
故:
R_gg (τ)=(1+α^2 ) R_ff (τ)+αR_ff (τ-∆)+αR_ff*
容易看出有回声的信号经过相关运算,将会出现三个明显的突出,中间是与自身进行相关,故是峰值,对称两边有次峰值,是原信号与带有损耗的回声信号相关所得。峰值与次峰值之比等于1+α^2与α之比,因此可以计算出α。
回声信号换写为:
g(n)可以看作是f(n)经过:
卷积所得。即:
而h(n)求得信号系统为:
故可构造出逆系统为:
然后用带有回声的信号g(n)与H ̂(n)卷积可得到不带回声的原信号f(n)。即:
实现
先读取带有回声的音频文件:
[y,Fs]=audioread('acho_voice.wav');
其中Fs是采样频率,y是音频数字信号。
用:
plot(y);
绘出音频信号图:
然后对此信号进行相关运算:
Ry=xcorr(y);
绘出Ry的图:
plot(Ry);
可以明显看出上述原理中的峰值与次峰值。用max() 函数进行计算:
[H1,X1]=max(Ry);
计算次峰值时需要去掉峰值,或者查看上图,看次峰值大概位置,对其进行区间限制。我是使用区间限制:
[H2,X2]=max(Ry(1:3.8*10^5));
然后就可以算出时延m与衰减系数k。
m=X1-X2;
[k]=solve((1+k^2)/k==H1/H2,k<1);
而且需要注意的是,限制区间后的取值是从你开始限制的那一点开始计算,比如限制区间是[10:25],那么15的值就会算为5。而且如果区间限制不是从1开始的话,时延差后需要再加1,比如换做:
[H2,X2]=max(Ry(3.7*10^5:3.8*10^5));
那么时延m的计算方法就是:
m=X1-(3.7*10^5+X2)+1;
然后用impz()冲激函数算出H ̂(n):
b=1;
a=[1,zeros(1,m-1),k];
h=impz(b,a); % b是分子,a是分母
然后用conv()卷积函数运算出无回声的信号即可:
g=conv(y,h);
Matlab代码
clear;
% Fs是采样频率,系统默认值:44100
% y是音频数字信号,n行1列,n是音频信号的时长乘以Fs
[y,Fs]=audioread('acho_voice.wav');
subplot(3,1,1);
plot(y);
title('acho_voice');
%对音频信号进行相关运算
Ry=xcorr(y);
subplot(3,1,2);
plot(Ry);
title('xcorr(acho_voice)');
%查看Ry的峰值H1 与次峰值H2 的位置,算出其值
[H1,X1]=max(Ry);
[H2,X2]=max(Ry(1:3.8*10^5));%如果区间不是从1开始,算时延时要注意差1
%计算出回声时延m
m=X1-X2;
m=double(m);
%根据峰值与次峰值之比等于1+k^2与k之比,算出衰减系数k
syms k;
[k]=solve((1+k^2)/k==H1/H2,k<1);%注意k有2个实根,要过滤掉K>1的解
k=double(k);
%impz冲击,b和a是分子分母系数的向量,h为该系统的脉冲向量
b=1;
a=[1,zeros(1,m-1),k];
h=impz(b,a); %impz()函数中的参数只能是double/single
%对回声信号与h进行卷积运算得到原信号
g=conv(y,h);
subplot(3,1,3);
plot(g);
title('noacho_voice');
%输出目的音频信号
audiowrite('D:\Code\MatLab\achoVoice\noacho_Vioce.wav',g,Fs);
遗留问题:我用的带有回声的音频是8秒,输出的无回声音频为13秒。