1 语音信号分帧
对于长N的语音信号按照如下式分帧
fn=(N-overlap)/inc
因为overlap=wlen-inc
所以fn=(N-wlen+inc)/inc
这里,wlen为每一帧的长度,inc表示后一帧对前一帧的位移量(帧移),忽略了帧移的问题,分帧后的结果发现并无大碍[1],两帧之间的重叠部分为overlap,N为读入语音文件数据的长度。
function f=enframe(x,win,inc)
nx=length(x(:)); % 取数据长度
nwin=length(win); % 取窗长
if (nwin == 1) % 判断窗长是否为1,若为1,即表示没有设窗函数
len = win; % 是,帧长=win
else
len = nwin; % 否,帧长=窗长
end
if (nargin < 3) % 如果只有两个参数,设帧inc=帧长
inc = len;
end
nf = fix((nx-len+inc)/inc); % 计算帧数
f=zeros(nf,len); % 初始化
indf= inc*(0:(nf-1)).'; % 设置每帧在x中的位移量位置
inds = (1:len); % 每帧数据对应1:len
f(:) = x(indf(:,ones(1,len))+inds(ones(nf,1),:)); % 对数据分帧
if (nwin > 1) % 若参数中包括窗函数,把每帧乘以窗函数
w = win(:)'; % 把win转成行数据
f = f .* w(ones(nf,1),:); % 乘窗函数
end
语音信号进过采样后实际上是无限长的,但处理中进行分帧,相当于乘以一个有限长的窗函数。在语音信号中,窗函数常用的有三种:矩形窗、海宁窗和汉明窗。窗函数一般具有低通性,这里需要注意两个名词,主瓣带宽和旁瓣衰减。
2 语音短时时域处理
2.1 短时能量和短时平均幅度
第i帧语音信号yi(n)的短时能量可以用如下程序表示:
for i=1 : fn
u=X(:,i); % 取出一帧
u2=u.*u; % 求出能量
En(i)=sum(u2); % 对一帧累加求和
end
平均幅度用如下程序表示:
for i=1 : fn
u=X(:,i); % 取出一帧
u2=abs(u); % 幅度
En(i)=sum(u2); % 对一帧累加求和
end
短时能量和短时平均幅度主要用途有:区分浊音段与清音段,因为浊音时的短时能量比清音时大得多;区分声母与韵母的分界和无话段与有话段的分界。
2.2 短时平均过零率
程序表示为:
for i=1:fn
z=X(:,i); % 取得一帧数据
for j=1: (wlen- 1) ; % 在一帧内寻找过零点
if z(j)* z(j+1)< 0 % 判断是否为过零点
zcr1(i)=zcr1(i)+1; % 是过零点,记录1次
end
end
end
因为高频意味着高的短时平均过零率,低频意味着低的短时平均过零率。所以,浊音时具有较低的过零率,清音时具有较高的过零率。当然,这里的高度只是相对的。
用途:利用短时过零率可以从背景噪音中找到语音信号,可用于判断无话段和有话段的起始点和终点位置。在背景噪音较小时,平均能量识别较为有效;而背景噪声较大时,短时平均过零率识别较为有效。
2.3 短时自相关函数
u=X(:,i); %取出一帧
R=xcorr(u); %利用xcorr函数求出自相关函数
R=R(wlen:end);%只取k值为正值的自相关函数
用途:主要用于端点检测和基音的提取,在韵母基音频率整数倍处将出现峰值特性,而声母将不会看到明显峰值。
2.4 短时平均幅度差函数
u=X(:,i); %取出一帧
for k=1:wlen
amdfVec(k)=sum(abs(u(k:end)-u(1:end-l+1)));
end
用途:可以用于基音周期检测,提取基音频率。
3 短时频域处理
function d=stftms(x,win,nfft,inc)
if length(win)==1 % 判断有否设置窗函数
wlen=win; % 否,设帧长
win=hanning(wlen); % 设置窗函数
else
wlen=length(win); % 设帧长
end
x=x(:); win=win(:); % 把x和win都变为列数组
s = length(x); % 计算x的长度
c = 1;
d = zeros((1+nfft/2),1+fix((s-wlen)/inc)); % 初始化输出数组
for b = 0:inc:(s-wlen) % 设置循环
u = win.*x((b+1):(b+wlen)); % 取来一帧数据加窗
t = fft(u,nfft); % 进行傅里叶变换
d(:,c) = t(1:(1+nfft/2)); % 取1到1+nfft/2之间的谱值
c = c+1; % 改变帧数,求取下一帧
end;
语谱图的横坐标是时间,纵坐标是频率,坐标点值为语音数据能量。由于是采用二维平面表达三维信息,所以能量值的大小是通过颜色来表示的,颜色深,表示该点的语音能量越强。
语谱图中有明显的一条条横方向的条纹,我们称为“声纹”,有很多应用。条纹的地方实际是颜色深的点聚集的地方,随时间延续,就延长成条纹,也就是表示语音中频率值为该点横坐标值的能量较强,在整个语音中所占比重大,那么相应影响人感知的效果要强烈得多。而一般语音中数据是周期性的,所以,能量强点的频率分布是频率周期的,即存在300Hz强点,则一般在n*300Hz点也会出现强点,所以我们看到的语谱图都是条纹状的。
画语谱图需要用带matlab自带函数“imagesc”。
frameTime=(((1:fn)-1)*inc+wlen/2)/Fs; % 计算每帧对应的时间
W2=wlen/2+1; n2=1:W2;
freq=(n2-1)*Fs/wlen; % 计算FFT后的频率刻度
Y=fft(y); % 短时傅里叶变换
imagesc(frameTime,freq,abs(Y(n2,:))); % 画出Y的图像
在信号经过DFT(FFT)后得到了信号的频谱,而要反应信号的功率经常用信号的功率谱密度。这里,我们使用pwelch方法计算短时功率谱密度函数。
function [Pxx] = pwelch_2(x, nwind, noverlap, w_nwind, w_noverlap, nfft)
% 计算短时功率谱密度函数
% x是信号,nwind是每帧长度,noverlap是每帧重叠的样点数
% w_nwind是每段的窗函数,或相应的段长,
% w_noverlap是每段之间的重叠的样点数,nfft是FFT的长度
x=x(:);
inc=nwind-noverlap; % 计算帧移
X=enframe(x,nwind,inc)'; % 分帧
frameNum=size(X,2); % 计算帧数
%用pwelch函数对每帧计算功率谱密度函数
for k=1 : frameNum
Pxx(:,k)=pwelch(X(:,k),w_nwind,w_noverlap,nfft);
end
这里,pwelch=abs(fft(x,nfft)).^2/N。最后,再用画语谱图的函数去画出函数谱图。
[Pxx] = pwelch_2(wavin0, nwind, noverlap, w_nwind, w_noverlap, nfft);
frameNum=size(Pxx,2); % 取来帧数
frameTime=frame2time(frameNum,nfft,inc,fs); % 计算每帧对应的时间
freq=(0:nfft/2)*fs/nfft; % 计算频率刻度
% 作图
imagesc(frameTime,freq,Pxx); axis xy
ylabel('频率/Hz');
xlabel('时间/s');
title('短时功率谱密度函数')