《MATLAB语音信号分析与合成(第二版)》是中科院声学所的大佬宋知用老师数十年经验积累下的呕心之作,对于语音信号处理相关感兴趣的同学,日后希望在语音信号分析、处理与合成相关领域进行一定研究的话,可以以此进行入门。
语音信号处理是数字信号处理的一个重要分支。本书含有许多数字信号处理的方法和 MATLAB函数。 全书共10章。第1-4章介绍语音信号处理的一些基本分析方法和手段,以及相应的MATLAB函数;第5-9章介绍语音信号预处理和特征的提取,包括消除趋势项和基本的减噪方法,以及端点检测、基音的提取和共 振峰的提取,并利用语音信号处理的基本方法,给出了多种提取方法和相应的 MATLAB程序;第10章结合 各种参数的检测介绍了语音信号的合成、语音信号的变速和变调处理,还介绍了时域基音同步叠加( TD PSOLA)的语音合成,并给出了相应的MATLAB程序。附录A中给出了调试复杂程序的方法和思路。 本书可作为从事语音信号处理的本科高年级学生、研究生或科研工程技术人员的辅助读物,也可作为从 事信号处理研究与应用的科研工程技术人员的参考用书。
我的研究生导师的主攻方向就是语音信号处理相关,虽然自己研究生期间的大论文方向是数字图像处理,但所谓语音图像不分家,自己在老师的研究生主讲课小波变换上虽然划水,但在后期导师的语音信号处理的课程设计和工程应用上自己在语音上还算入了一点小门道,在结课测试中拿到了小组第一,导师还特地发了三百大洋的伙食经费以资鼓励。
这次重新捡起语音识别,正好入手了宋老师的这本书,算是自己重新复习一遍吧,主要以介绍各章节中源码为主,这是本书的第六章的前13个仿真应用实例,话不多说,开始!
书中经常会调用的一些函数(自编函数或取自其他应用工具箱中的函数)已集中在basic_tbx工具箱中,在运行本书的程序前请把该工具箱设置(用set path设置)在工作路径下;
当要运行EMD处理时,要把emd工具箱设置在工作路径下;
当要运行主体延伸基音检测时,要把Pitch_ztlib工具箱设置在工作路径下;
当要进行时域基音同步叠加语音合成时,要把psola_lib工具箱设置在工作路径下;
当要应用本书提供的语音数据时,最好把speech_signal设置在工作路径下。
本书的所有函数和程序都在MATLAB R2009a版本下调试通过。(我用的是MATLAB2015b,有些函数已经更新,所以我会进行修改,以便调试通过)
路径设置的方法如下:
打开MATLAB,点击“主页”,找到设置路径
将上述文件夹路径全部添加到MATLAB搜索路径中
添加完毕,保存,开始仿真。
%
% pr6_1_1
clear all; clc; close all;
filedir=[]; % 指定文件路径
filename='bluesky1.wav'; % 指定文件名
fle=[filedir filename] % 构成路径和文件名的字符串
% [x,fs]=wavread(fle); % 读入数据文件
[x,fs]=audioread(fle); % 读入数据文件
x=x/max(abs(x)); % 幅度归一化
N=length(x); % 取信号长度
time=(0:N-1)/fs; % 计算时间
pos = get(gcf,'Position'); % 作图
set(gcf,'Position',[pos(1), pos(2)-100,pos(3),(pos(4)-200)]);
plot(time,x,'k');
title('男声“蓝天,白云,碧绿的大海”的端点检测');
ylabel('幅值'); axis([0 max(time) -1 1]); grid;
xlabel('时间/s');
wlen=200; inc=80; % 分帧参数
IS=0.1; overlap=wlen-inc; % 设置IS
NIS=fix((IS*fs-wlen)/inc +1); % 计算NIS
fn=fix((N-wlen)/inc)+1; % 求帧数
frameTime=frame2time(fn, wlen, inc, fs);% 计算每帧对应的时间
[voiceseg,vsl,SF,NF]=vad_ezm1(x,wlen,inc,NIS); % 端点检测
for k=1 : vsl % 画出起止点位置
nx1=voiceseg(k).begin; nx2=voiceseg(k).end;
nxl=voiceseg(k).duration;
fprintf('%4d %4d %4d %4d\n',k,nx1,nx2,nxl);
line([frameTime(nx1) frameTime(nx1)],[-1.5 1.5],'color','k','LineStyle','-');
line([frameTime(nx2) frameTime(nx2)],[-1.5 1.5],'color','k','LineStyle','--');
end
function [voiceseg,vsl,SF,NF]=vad_ezm1(x,wlen,inc,NIS)
x=x(:); % 把x转换成列数组
maxsilence = 15; % 初始化
minlen = 5;
status = 0;
count = 0;
silence = 0;
y=enframe(x,wlen,inc)'; % 分帧
fn=size(y,2); % 帧数
amp=sum(y.^2); % 求取短时平均能量
zcr=zc2(y,fn); % 计算短时平均过零率
ampth=mean(amp(1:NIS)); % 计算初始无话段区间能量和过零率的平均值
zcrth=mean(zcr(1:NIS));
amp2=2*ampth; amp1=4*ampth; % 设置能量和过零率的阈值
zcr2=2*zcrth;
%开始端点检测
xn=1;
for n=1:fn
switch status
case {0,1} % 0 = 静音, 1 = 可能开始
if amp(n) > amp1 % 确信进入语音段
x1(xn) = max(n-count(xn)-1,1);
status = 2;
silence(xn) = 0;
count(xn) = count(xn) + 1;
elseif amp(n) > amp2 | ... % 可能处于语音段
zcr(n) > zcr2
status = 1;
count(xn) = count(xn) + 1;
else % 静音状态
status = 0;
count(xn) = 0;
x1(xn)=0;
x2(xn)=0;
end
case 2, % 2 = 语音段
if amp(n) > amp2 & ... % 保持在语音段
zcr(n) > zcr2
count(xn) = count(xn) + 1;
silence(xn) = 0;
else % 语音将结束
silence(xn) = silence(xn)+1;
if silence(xn) < maxsilence % 静音还不够长,语音尚未结束
count(xn) = count(xn) + 1;
elseif count(xn) < minlen % 语音长度太短,认为是静音或噪声
status = 0;
silence(xn) = 0;
count(xn) = 0;
else % 语音结束
status = 3;
x2(xn)=x1(xn)+count(xn);
end
end
case 3, % 语音结束,为下一个语音准备
status = 0;
xn=xn+1;
count(xn) = 0;
silence(xn)=0;
x1(xn)=0;
x2(xn)=0;
end
end
el=length(x1);
if x1(el)==0, el=el-1; end % 获得x1的实际长度
if x2(el)==0 % 如果x2最后一个值为0,对它设置为fn
fprintf('Error: Not find endding point!\n');
x2(el)=fn;
end
SF=zeros(1,fn); % 按x1和x2,对SF和NF赋值
NF=ones(1,fn);
for i=1 : el
SF(x1(i):x2(i))=1;
NF(x1(i):x2(i))=0;
end
speechIndex=find(SF==1); % 计算voiceseg
voiceseg=findSegment(speechIndex);
vsl=length(voiceseg);
%
% pr6_2_1
clear all; clc; close all;
filedir=[]; % 指定文件路径
filename='bluesky1.wav'; % 指定文件名
fle=[filedir filename] % 构成路径和文件名的字符串
% [xx,fs]=wavread(fle); % 读入数据文件
[xx,fs]=audioread(fle); % 读入数据文件
xx=xx/max(abs(xx)); % 幅度归一化
N=length(xx); % 取信号长度
time=(0:N-1)/fs; % 计算时间刻度
x=Gnoisegen(xx,20); % 把白噪声叠加到信号上
wlen=200; inc=80; % 设置帧长和帧移
IS=0.25; overlap=wlen-inc; % 设置前导无话段长度
NIS=fix((IS*fs-wlen)/inc +1); % 计算前导无话段帧数
fn=fix((N-wlen)/inc)+1; % 求出总帧数
frameTime=frame2time(fn, wlen, inc, fs);% 计算每帧对应的时间
[voiceseg,vsl,SF,NF]=vad_ezr(x,wlen,inc,NIS); % 端点检测
% 作图
subplot 211; plot(time,xx,'k'); hold on
title('纯语音男声“蓝天,白云,碧绿的大海”波形');
ylabel('幅值'); axis([0 max(time) -1 1]); xlabel('(a)');
for k=1 : vsl
nx1=voiceseg(k).begin; nx2=voiceseg(k).end;
fprintf('%4d %4d %4d\n',k,nx1,nx2);
line([frameTime(nx1) frameTime(nx1)],[-1.5 1.5],'color','k','LineStyle','-');
line([frameTime(nx2) frameTime(nx2)],[-1.5 1.5],'color','k','LineStyle','--');
end
subplot 212; plot(time,x,'k');
title('加噪语音波形(信噪比20dB)');
ylabel('幅值'); axis([0 max(time) -1 1]);
xlabel(['时间/s' 10 '(b)']);
function [voiceseg,vsl,SF,NF]=vad_ezr(x,wlen,inc,NIS)
x=x(:); % 把x转成列数组
maxsilence = 15; % 初始化
minlen = 5;
status = 0;
count = 0;
silence = 0;
%计算短时能量
y=enframe(x,wlen,inc)'; % 分帧
fn=size(y,2); % 帧数
amp=sum(y.^2); % 求取短时平均能量
zcr=zc2(y,fn); % 计算短时平均过零率
ampth=mean(amp(1:NIS)); % 计算初始无话段区间能量和过零率的平均值
zcrth=mean(zcr(1:NIS));
amp2=2*ampth; amp1=4*ampth; % 设置能量和过零率的值
zcr2=0.8*zcrth;
%开始端点检测
xn=1;
for n=1:fn
fprintf('%4d %5.4f %5.4f ',n,amp(n),zcr(n));
switch status
case {0,1} % 0 = 静音, 1 = 可能开始
if amp(n) > amp1 % 确信进入语音段
x1(xn) = max(n-count(xn)-1,1);
status = 2;
silence(xn) = 0;
count(xn) = count(xn) + 1;
elseif amp(n) > amp2 | ... % 可能处于语音段
zcr(n) < zcr2
status = 1;
count(xn) = count(xn) + 1;
else % 静音状态
status = 0;
count(xn) = 0;
x1(xn)=0;
x2(xn)=0;
end
case 2, % 2 = 语音段
if amp(n) > amp2 | ... % 保持在语音段
zcr(n) < zcr2
count(xn) = count(xn) + 1;
else % 语音将结束
silence(xn) = silence(xn)+1;
if silence(xn) < maxsilence % 静音还不够长,尚未结束
count(xn) = count(xn) + 1;
elseif count(xn) < minlen % 语音长度太短,认为是噪声
status = 0;
silence(xn) = 0;
count(xn) = 0;
else % 语音结束
status = 3;
x2(xn)=x1(xn)+count(xn);
end
end
case 3, % 语音结束,为下一个语音准备
status = 0;
xn=xn+1;
count(xn) = 0;
silence(xn)=0;
x1(xn)=0;
x2(xn)=0;
end
fprintf('%4d \n',status);
end
el=length(x1);
if x1(el)==0, el=el-1; end % 获得x1的实际长度
if x2(el)==0 % 如果x2最后一个值为0,对它设置为fn
fprintf('Error: Not find endding point!\n');
x2(el)=fn;
end
SF=zeros(1,fn); % 按x1和x2,对SF和NF赋值
NF=ones(1,fn);
for i=1 : el
SF(x1(i):x2(i))=1;
NF(x1(i):x2(i))=0;
end
speechIndex=find(SF==1); % 计算voiceseg
voiceseg=findSegment(speechIndex);
vsl=length(voiceseg);
%
% pr6_2_2
clear all; clc; close all;
filedir=[]; % 指定文件路径
filename='bluesky1.wav'; % 指定文件名
fle=[filedir filename] % 构成路径和文件名的字符串
% [xx,fs]=wavread(fle); % 读入数据文件
[xx,fs]=audioread(fle); % 读入数据文件
xx=xx/max(abs(xx)); % 幅度归一化
N=length(xx); % 取信号长度
time=(0:N-1)/fs; % 计算时间刻度
SNR=10; % 信噪比
x=Gnoisegen(xx,SNR); % 把白噪声叠加到信号上
wlen=200; inc=80; % 设置帧长和帧移
IS=0.25; overlap=wlen-inc; % 设置前导无话段长度
NIS=fix((IS*fs-wlen)/inc +1); % 计算前导无话段帧数
y=enframe(x,wlen,inc)'; % 分帧
fn=size(y,2); % 帧数
amp=sum(y.^2); % 求取短时平均能量
zcr=zc2(y,fn); % 计算短时平均过零率
ampm = multimidfilter(amp,5); % 中值滤波平滑处理
zcrm = multimidfilter(zcr,5);
ampth=mean(ampm(1:NIS)); % 计算初始无话段区间能量和过零率的平均值
zcrth=mean(zcrm(1:NIS));
amp2=1.1*ampth; amp1=1.3*ampth; % 设置能量和过零率的阈值
zcr2=0.9*zcrth;
frameTime=frame2time(fn, wlen, inc, fs);% 计算各帧对应的时间
[voiceseg,vsl,SF,NF]=vad_param2D_revr(ampm,zcrm,amp2,amp1,zcr2);% 端点检测
% 作图
subplot 211; plot(time,xx,'k');
title('纯语音男声“蓝天,白云,碧绿的大海”波形');
ylabel('幅值'); axis([0 max(time) -1 1]);
for k=1 : vsl
nx1=voiceseg(k).begin; nx2=voiceseg(k).end;
fprintf('%4d %4d %4d\n',k,nx1,nx2);
line([frameTime(nx1) frameTime(nx1)],[-1.5 1.5],'color','k','LineStyle','-');
line([frameTime(nx2) frameTime(nx2)],[-1.5 1.5],'color','k','LineStyle','--');
end
subplot 212; plot(time,x,'k');
title(['加噪语音波形(信噪比' num2str(SNR) 'dB)']);
ylabel('幅值'); axis([0 max(time) -1 1]);
xlabel('时间/s');
function [voiceseg,vsl,SF,NF]=vad_param2D_revr(dst1,dst2,T1,T2,T3,T4)
fn=length(dst1); % 取帧数
maxsilence = 8; % 初始化
minlen = 5;
status = 0;
count = 0;
silence = 0;
%开始端点检测
xn=1;
for n=1:fn
switch status
case {0,1} % 0 = 静音, 1 = 可能开始
if dst1(n) > T2 | ... % 确信进入语音段
( nargin==6 & dst2(n) < T4 )
x1(xn) = max(n-count(xn)-1,1);
status = 2;
silence(xn) = 0;
count(xn) = count(xn) + 1;
elseif dst1(n) > T1 | ... % 可能处于语音段
dst2(n) < T3
status = 1;
count(xn) = count(xn) + 1;
else % 静音状态
status = 0;
count(xn) = 0;
x1(xn)=0;
x2(xn)=0;
end
case 2, % 2 = 语音段
if dst1(n) > T1 | ... % 保持在语音段
dst2(n) < T3
count(xn) = count(xn) + 1;
silence(xn) = 0;
else % 语音将结束
silence(xn) = silence(xn)+1;
if silence(xn) < maxsilence % 静音还不够长,尚未结束
count(xn) = count(xn) + 1;
elseif count(xn) < minlen % 语音长度太短,认为是噪声
status = 0;
silence(xn) = 0;
count(xn) = 0;
else % 语音结束
status = 3;
x2(xn)=x1(xn)+count(xn);
end
end
case 3, % 语音结束,为下一个语音准备
status = 0;
xn=xn+1;
count(xn) = 0;
silence(xn)=0;
x1(xn)=0;
x2(xn)=0;
end
end
el=length(x1);
if x1(el)==0, el=el-1; end % 获得x1的实际长度
if x2(el)==0 % 如果x2最后一个值为0,对它设置为fn
fprintf('Error: Not find endding point!\n');
x2(el)=fn;
end
SF=zeros(1,fn); % 按x1和x2,对SF和NF赋值
NF=ones(1,fn);
for i=1 : el
SF(x1(i):x2(i))=1;
NF(x1(i):x2(i))=0;
end
speechIndex=find(SF==1); % 计算voiceseg
voiceseg=findSegment(speechIndex);
vsl=length(voiceseg);
%
% pr6_2_3
clear all; clc; close all;
filedir=[]; % 指定文件路径
filename='bluesky1.wav'; % 指定文件名
fle=[filedir filename] % 构成路径和文件名的字符串
% [xx,fs]=wavread(fle); % 读入数据文件
[xx,fs]=audioread(fle); % 读入数据文件
x=xx/max(abs(xx)); % 幅度归一化
N=length(xx); % 取信号长度
time=(0:N-1)/fs; % 计算时间刻度
wlen=200; inc=80; % 设置帧长和帧移
IS=0.25; overlap=wlen-inc; % 设置前导无话段长度
NIS=fix((IS*fs-wlen)/inc +1); % 计算前导无话段帧数
y=enframe(x,wlen,inc)'; % 分帧
etemp=sum(y.^2); % 求取短时平均能量
etemp=etemp/max(etemp); % 能量幅值归一化
fn=size(y,2); % 帧数
T1=0.002; % 设置阈值
T2=0.01;
frameTime=frame2time(fn, wlen, inc, fs);% 计算各帧对应的时间
[voiceseg,vsl,SF,NF]=vad_param1D(etemp,T1,T2);% 用一个参数端点检测
% 作图
subplot 211; plot(time,x,'k'); hold on
title('纯语音男声“蓝天,白云,碧绿的大海”波形');
ylabel('幅值'); axis([0 max(time) -1 1]);
for k=1 : vsl
nx1=voiceseg(k).begin; nx2=voiceseg(k).end;
fprintf('%4d %4d %4d\n',k,nx1,nx2);
line([frameTime(nx1) frameTime(nx1)],[-1 1],'color','k','LineStyle','-');
line([frameTime(nx2) frameTime(nx2)],[-1 1],'color','k','LineStyle','--');
end
subplot 212; plot(frameTime,etemp,'k');
title('语音短时能量图');
ylabel('幅值'); axis([0 max(time) 0 1]);
xlabel('时间/s');
line([0 max(time)],[T1 T1],'color','k','LineStyle','-');
line([0 max(time)],[T2 T2],'color','k','LineStyle','--');
function [voiceseg,vsl,SF,NF]=vad_param1D(dst1,T1,T2)
fn=size(dst1,2); % 取得帧数
maxsilence = 8; % 初始化
minlen = 5;
status = 0;
count = 0;
silence = 0;
%开始端点检测
xn=1;
for n=2:fn
switch status
case {0,1} % 0 = 静音, 1 = 可能开始
if dst1(n) > T2 % 确信进入语音段
x1(xn) = max(n-count(xn)-1,1);
status = 2;
silence(xn) = 0;
count(xn) = count(xn) + 1;
elseif dst1(n) > T1 % 可能处于语音段
% zcr(n) < zcr2
status = 1;
count(xn) = count(xn) + 1;
else % 静音状态
status = 0;
count(xn) = 0;
x1(xn)=0;
x2(xn)=0;
end
case 2, % 2 = 语音段
if dst1(n) > T1 % 保持在语音段
count(xn) = count(xn) + 1;
silence(xn) = 0;
else % 语音将结束
silence(xn) = silence(xn)+1;
if silence(xn) < maxsilence % 静音还不够长,尚未结束
count(xn) = count(xn) + 1;
elseif count(xn) < minlen % 语音长度太短,认为是噪声
status = 0;
silence(xn) = 0;
count(xn) = 0;
else % 语音结束
status = 3;
x2(xn)=x1(xn)+count(xn);
end
end
case 3, % 语音结束,为下一个语音准备
status = 0;
xn=xn+1;
count(xn) = 0;
silence(xn)=0;
x1(xn)=0;
x2(xn)=0;
end
end
el=length(x1);
if x1(el)==0, el=el-1; end % 获得x1的实际长度
if el==0, return; end
if x2(el)==0 % 如果x2最后一个值为0,对它设置为fn
fprintf('Error: Not find endding point!\n');
x2(el)=fn;
end
SF=zeros(1,fn); % 按x1和x2,对SF和NF赋值
NF=ones(1,fn);
for i=1 : el
SF(x1(i):x2(i))=1;
NF(x1(i):x2(i))=0;
end
speechIndex=find(SF==1); % 计算voiceseg
voiceseg=findSegment(speechIndex);
vsl=length(voiceseg);
%
% pr6_3_1
clear all; clc; close all;
run Set_I % 基本设置
run PART_I % 读入数据,分帧等准备
for k=2 : fn % 计算自相关函数
u=y(:,k);
ru=xcorr(u);
Ru(k)=max(ru);
end
Rum=multimidfilter(Ru,10); % 平滑处理
Rum=Rum/max(Rum); % 归一化
thredth=max(Rum(1:NIS)); % 计算阈值
T1=1.1*thredth;
T2=1.3*thredth;
[voiceseg,vsl,SF,NF]=vad_param1D(Rum,T1,T2);% 自相关函数的端点检测
% 作图
subplot 311; plot(time,x,'k');
title('纯语音波形');
ylabel('幅值'); axis([0 max(time) -1 1]);
subplot 312; plot(time,signal,'k');
title(['加噪语音波形(信噪比' num2str(SNR) 'dB)']);
ylabel('幅值'); axis([0 max(time) -1 1]);
subplot 313; plot(frameTime,Rum,'k');
title('短时自相关函数'); axis([0 max(time) 0 1.2]);
xlabel('时间/s'); ylabel('幅值');
line([0,frameTime(fn)], [T1 T1], 'color','k','LineStyle','--');
line([0,frameTime(fn)], [T2 T2], 'color','k','LineStyle','-');
for k=1 : vsl % 标出语音端点
nx1=voiceseg(k).begin; nx2=voiceseg(k).end;
fprintf('%4d %4d %4d\n',k,nx1,nx2);
subplot 311;
line([frameTime(nx1) frameTime(nx1)],[-1 1],'color','k','LineStyle','-');
line([frameTime(nx2) frameTime(nx2)],[-1 1],'color','k','LineStyle','--');
end
%Set_I.m
% Set_I
IS=0.25; % 设置前导无话段长度
wlen=200; % 设置帧长为25ms
inc=80; % 求帧移
filedir=[]; % 设置文件路径
filename='bluesky1.wav'; % 设置文件名称
fle=[filedir filename] % 构成文件路径和名称
SNR=10; % 设置信噪比
% PART_I.m
% PART_I
% [xx,fs]=wavread(fle); % 读入数据
[xx,fs]=audioread(fle); % 读入数据
xx=xx-mean(xx); % 消除直流分量
x=xx/max(abs(xx)); % 幅值归一化
N=length(x); % 取信号长度
time=(0:N-1)/fs; % 设置时间
signal=Gnoisegen(x,SNR); % 叠加噪声
wnd=hamming(wlen); % 设置窗函数
overlap=wlen-inc; % 求重叠区长度
NIS=fix((IS*fs-wlen)/inc +1); % 求前导无话段帧数
y=enframe(signal,wnd,inc)'; % 分帧
fn=size(y,2); % 求帧数
frameTime=frame2time(fn, wlen, inc, fs);% 计算各帧对应的时间
%
% pr6_3_2
clear all; clc; close all;
run Set_I % 基本设置
run PART_I % 读入数据,分帧等准备
for k=2 : fn % 计算互相关函数
u1=y(:,k-1);
u2=y(:,k);
ru=xcorr(u1,u2);
Ru(k)=max(ru);
end
Rum=multimidfilter(Ru,10); % 平滑处理
Rum=Rum/max(Rum); % 归一化
thredth=max(Rum(2:NIS)); % 计算阈值
T1=1.1*thredth;
T2=1.3*thredth;
[voiceseg,vsl,SF,NF]=vad_param1D(Rum,T1,T2);% 互相关函数的端点检测
% 作图
subplot 311; plot(time,x,'k');
title('纯语音波形');
ylabel('幅值'); axis([0 max(time) -1 1]);
subplot 312; plot(time,signal,'k');
title(['加噪语音波形(信噪比' num2str(SNR) 'dB)']);
ylabel('幅值'); axis([0 max(time) -1 1]);
subplot 313; plot(frameTime,Rum,'k');
title('短时自相关函数'); axis([0 max(time) 0 1.2]);
xlabel('时间/s'); ylabel('幅值');
line([0,frameTime(fn)], [T1 T1], 'color','k','LineStyle','--');
line([0,frameTime(fn)], [T2 T2], 'color','k','LineStyle','-');
for k=1 : vsl % 标出语音端点
nx1=voiceseg(k).begin; nx2=voiceseg(k).end;
fprintf('%4d %4d %4d\n',k,nx1,nx2);
subplot 311;
line([frameTime(nx1) frameTime(nx1)],[-1 1],'color','k','LineStyle','-');
line([frameTime(nx2) frameTime(nx2)],[-1 1],'color','k','LineStyle','--');
end
%
% pr6_3_3
clear all; clc; close all;
run Set_I % 基本设置
run PART_I % 读入数据,分帧等准备
[n,Wn]=buttord(300/(fs/2),600/(fs/2),3,20); % 计算滤波器阶数和带宽
[bs,as]=butter(n,Wn); % 求取数字滤波器系数
for k=1 : fn
u=y(:,k); % 取一帧数据
ru=xcorr(u); % 计算自相关函数
rnu=ru/max(ru); % 归一化
rpu=filter(bs,as,rnu); % 数字滤波
Ru(k)=max(rpu); % 寻找最大值
end
Rum=multimidfilter(Ru,10); % 平滑处理
thredth=max(Rum(1:NIS)); % 设置阈值
T1=1.2*thredth;
T2=1.5*thredth;
[voiceseg,vsl,SF,NF]=vad_param1D(Rum,T1,T2); % 单参数双门限端点检测
% 作图
subplot 311; plot(time,x,'k');
title('纯语音波形');
ylabel('幅值'); axis([0 max(time) -1 1]);
subplot 312; plot(time,signal,'k');
title(['加噪语音波形(信噪比' num2str(SNR) 'dB)']);
ylabel('幅值'); axis([0 max(time) -1 1]);
subplot 313; plot(frameTime,Rum,'k');
title('短时归一化自相关函数'); grid; ylim([0 1.2]);
xlabel('时间/s'); ylabel('幅值');
line([0,frameTime(fn)], [T1 T1], 'color','k','LineStyle','--');
line([0,frameTime(fn)], [T2 T2], 'color','k','LineStyle','-');
% 标出语音端点
for k=1 : vsl
nx1=voiceseg(k).begin; nx2=voiceseg(k).end;
fprintf('%4d %4d %4d\n',k,nx1,nx2);
subplot 311;
line([frameTime(nx1) frameTime(nx1)],[-1 1],'color','k','LineStyle','-');
line([frameTime(nx2) frameTime(nx2)],[-1 1],'color','k','LineStyle','--');
subplot 313;
line([frameTime(nx1) frameTime(nx1)],[0 1.2],'color','k','LineStyle','-');
line([frameTime(nx2) frameTime(nx2)],[0 1.2],'color','k','LineStyle','--');
end
%
% pr6_3_4
clear all; clc; close all;
run Set_I % 基本设置
run PART_I % 读入数据,分帧等准备
for k=1 : fn
u=y(:,k); % 取一帧数据
ru=xcorr(u); % 计算自相关函数
ru0=ru(wlen); % 取主峰值
ru1=max(ru(wlen+17:wlen+133)); % 取第一个副峰值
R1(k)=ru0/ru1; % 计算主副峰比值
end
Rum=multimidfilter(R1,20); % 平滑处理
Rum=Rum/max(Rum); % 数值归一化
alphath=mean(Rum(1:NIS)); % 设置阈值
T1=0.95*alphath;
T2=0.75*alphath;
[voiceseg,vsl,SF,NF]=vad_param1D_revr(Rum,T1,T2);% 单参数双门限反向端点检测
% 作图
subplot 311; plot(time,x,'k');
title('纯语音波形');
ylabel('幅值'); axis([0 max(time) -1 1]);
subplot 312; plot(time,signal,'k');
title(['加噪语音波形(信噪比' num2str(SNR) 'dB)']);
ylabel('幅值'); axis([0 max(time) -1 1]);
subplot 313; plot(frameTime,Rum,'k');
title('短时自相关函数主副峰值比'); axis([0 max(time) 0 1.2]);
xlabel('时间/s'); ylabel('幅值');
line([0,frameTime(fn)], [T1 T1], 'color','k','LineStyle','--');
line([0,frameTime(fn)], [T2 T2], 'color','k','LineStyle','-');
% 标出语音端点
for k=1 : vsl
nx1=voiceseg(k).begin; nx2=voiceseg(k).end;
fprintf('%4d %4d %4d\n',k,nx1,nx2);
subplot 311;
line([frameTime(nx1) frameTime(nx1)],[-1 1],'color','k','LineStyle','-');
line([frameTime(nx2) frameTime(nx2)],[-1 1],'color','k','LineStyle','--');
subplot 313;
line([frameTime(nx1) frameTime(nx1)],[0 1.2],'color','k','LineStyle','-');
line([frameTime(nx2) frameTime(nx2)],[0 1.2],'color','k','LineStyle','--');
end
function [voiceseg,vsl,SF,NF]=vad_param1D_revr(dst1,T1,T2)
fn=length(dst1); % 取得帧数
maxsilence = 8; % 初始化
minlen = 5;
status = 0;
count = 0;
silence = 0;
%开始端点检测
xn=1;
for n=2:fn
switch status
case {0,1} % 0 = 静音, 1 = 可能开始
if dst1(n) < T2 % 确信进入语音段
x1(xn) = max(n-count(xn)-1,1);
status = 2;
silence(xn) = 0;
count(xn) = count(xn) + 1;
elseif dst1(n) < T1 % 可能处于语音段
status = 1;
count(xn) = count(xn) + 1;
else % 静音状态
status = 0;
count(xn) = 0;
x1(xn)=0;
x2(xn)=0;
end
case 2, % 2 = 语音段
if dst1(n) < T1 % 保持在语音段
count(xn) = count(xn) + 1;
silence(xn) = 0;
else % 语音将结束
silence(xn) = silence(xn)+1;
if silence(xn) < maxsilence % 静音还不够长,尚未结束
count(xn) = count(xn) + 1;
elseif count(xn) < minlen % 语音长度太短,认为是噪声
status = 0;
silence(xn) = 0;
count(xn) = 0;
else % 语音结束
status = 3;
x2(xn)=x1(xn)+count(xn);
end
end
case 3, % 语音结束,为下一个语音准备
status = 0;
xn=xn+1;
count(xn) = 0;
silence(xn)=0;
x1(xn)=0;
x2(xn)=0;
end
end
el=length(x1);
if x1(el)==0, el=el-1; end % 获得x1的实际长度
if el==0, return; end
if x2(el)==0 % 如果x2最后一个值为0,对它设置为fn
fprintf('Error: Not find endding point!\n');
x2(el)=fn;
end
SF=zeros(1,fn); % 按x1和x2,对SF和NF赋值
NF=ones(1,fn);
for i=1 : el
SF(x1(i):x2(i))=1;
NF(x1(i):x2(i))=0;
end
speechIndex=find(SF==1); % 计算voiceseg
voiceseg=findSegment(speechIndex);
vsl=length(voiceseg);
%
% pr6_3_5
clear all; clc; close all;
run Set_I % 基本设置
run PART_I % 读入数据,分帧等准备
Rw=zeros(2*wlen-1,1); % Rw初始化
for k=1 : NIS % 按式(6-3-6)计算Rw
u=y(:,k); % 取一帧数据
ru=xcorr(u); % 计算自相关函数
Rw=Rw+ru;
end
Rw=Rw/NIS;
Rw2=sum(Rw.*Rw); % 计算式(6-3-5)中分母内Rw的部分
for k=1 : fn
u=y(:,k); % 取一帧数据
ru=xcorr(u); % 计算自相关函数
Cm=sum(ru.*Rw); % 计算式(6-3-5)中分子部分
Cru=sum(ru.*ru); % 计算式(6-3-5)中分母内Ry的部分
Ru(k)=Cm/sqrt(Rw2*Cru); % 计算式(6-3-5)每帧的自相关函数余弦夹角
end
Rum=multimidfilter(Ru,10); % 平滑处理
alphath=mean(Rum(1:NIS)); % 设置阈值
T2=0.8*alphath; T1=0.9*alphath;
[voiceseg,vsl,SF,NF]=vad_param1D_revr(Rum,T1,T2); % 单参数双门限反向端点检测
% 作图
subplot 311; plot(time,x,'k');
title('纯语音波形');
ylabel('幅值'); axis([0 max(time) -1 1]);
subplot 312; plot(time,signal,'k');
title(['加噪语音波形(信噪比' num2str(SNR) 'dB)']);
ylabel('幅值'); axis([0 max(time) -1 1]);
subplot 313; plot(frameTime,Rum,'k');
title('短时自相关函数余弦夹角值'); axis([0 max(time) 0 1]);
xlabel('时间/s'); ylabel('幅值');
line([0,frameTime(fn)], [T1 T1], 'color','k','LineStyle','--');
line([0,frameTime(fn)], [T2 T2], 'color','k','LineStyle','-');
for k=1 : vsl % 标出语音端点
nx1=voiceseg(k).begin; nx2=voiceseg(k).end;
fprintf('%4d %4d %4d\n',k,nx1,nx2);
subplot 311;
line([frameTime(nx1) frameTime(nx1)],[-1 1],'color','k','LineStyle','-');
line([frameTime(nx2) frameTime(nx2)],[-1 1],'color','k','LineStyle','--');
subplot 313;
line([frameTime(nx1) frameTime(nx1)],[0 1.2],'color','k','LineStyle','-');
line([frameTime(nx2) frameTime(nx2)],[0 1.2],'color','k','LineStyle','--');
end
%
% pr6_4_1
clear all; clc; close all;
run Set_I % 基本设置
run PART_I % 读入数据,分帧等准备
Y=fft(y); % FFT变换
N2=wlen/2+1; % 取正频率部分
n2=1:N2;
Y_abs=abs(Y(n2,:)); % 取幅值
for k=1:fn % 计算每帧的频带方差
Dvar(k)=var(Y_abs(:,k))+eps;
end
dth=mean(Dvar(1:NIS)); % 求取阈值
T1=1.5*dth;
T2=3*dth;
[voiceseg,vsl,SF,NF]=vad_param1D(Dvar,T1,T2);% 频域方差双门限的端点检测
% 作图
subplot 311; plot(time,x,'k');
title('纯语音波形');
ylabel('幅值'); axis([0 max(time) -1 1]);
subplot 312; plot(time,signal,'k');
title('加噪语音波形(信噪比10dB)');
ylabel('幅值'); axis([0 max(time) -1 1]);
subplot 313; plot(frameTime,Dvar,'k');
title('短时频带方差值'); grid; ylim([0 1.2*max(Dvar)]);
xlabel('时间/s'); ylabel('幅值');
line([0,frameTime(fn)], [T1 T1], 'color','k','LineStyle','--');
line([0,frameTime(fn)], [T2 T2], 'color','k','LineStyle','-');
for k=1 : vsl % 标出语音端点
nx1=voiceseg(k).begin; nx2=voiceseg(k).end;
fprintf('%4d %4d %4d\n',k,nx1,nx2);
subplot 311;
line([frameTime(nx1) frameTime(nx1)],[-1 1],'color','k','LineStyle','-');
line([frameTime(nx2) frameTime(nx2)],[-1 1],'color','k','LineStyle','--');
subplot 313;
line([frameTime(nx1) frameTime(nx1)],[0 1.2*max(Dvar)],'color','k','LineStyle','-');
line([frameTime(nx2) frameTime(nx2)],[0 1.2*max(Dvar)],'color','k','LineStyle','--');
end
%
% pr6_4_2
clear all; clc; close all;
run Set_I % 基本设置
run PART_I % 读入数据,分帧等准备
Y=fft(y); % FFT变换
N2=wlen/2+1; % 取正频率部分
n2=1:N2;
Y_abs=abs(Y(n2,:)); % 取幅值
M=fix(N2/4); % 计算子带数
for k=1 : fn
for i=1 : M % 每个子带中有4条谱线
j=(i-1)*4+1;
SY(i,k)=Y_abs(j,k)+Y_abs(j+1,k)+Y_abs(j+2,k)+Y_abs(j+3,k);
end
Dvar(k)=var(SY(:,k)); % 计算每帧子带分离的频带方差
end
Dvarm=multimidfilter(Dvar,10); % 平滑处理
dth=mean(Dvarm(1:(NIS))); % 阈值计算
T1=1.5*dth;
T2=3*dth;
[voiceseg,vsl,SF,NF]=vad_param1D(Dvarm,T1,T2);% 频域方差双门限的端点检测
% 作图
subplot 311; plot(time,x,'k');
title('纯语音波形');
ylabel('幅值'); axis([0 max(time) -1 1]);
subplot 312; plot(time,signal,'k');
title('加噪语音波形(信噪比10dB)');
ylabel('幅值'); axis([0 max(time) -1 1]);
subplot 313; plot(frameTime,Dvar,'k');
title('短时均匀子带分离的频带方差值'); grid; ylim([0 1.2*max(Dvar)]);
xlabel('时间/s'); ylabel('幅值');
line([0,frameTime(fn)], [T1 T1], 'color','k','LineStyle','--');
line([0,frameTime(fn)], [T2 T2], 'color','k','LineStyle','-');
for k=1 : vsl % 标出语音端点
nx1=voiceseg(k).begin; nx2=voiceseg(k).end;
fprintf('%4d %4d %4d\n',k,nx1,nx2);
subplot 311;
line([frameTime(nx1) frameTime(nx1)],[-1 1],'color','k','LineStyle','-');
line([frameTime(nx2) frameTime(nx2)],[-1 1],'color','k','LineStyle','--');
subplot 313;
line([frameTime(nx1) frameTime(nx1)],[0 1.2*max(Dvar)],'color','k','LineStyle','-');
line([frameTime(nx2) frameTime(nx2)],[0 1.2*max(Dvar)],'color','k','LineStyle','--');
end
%
% pr6_4_3
clear all; clc; close all;
run Set_I % 基本设置
run PART_I % 读入数据,分帧等准备
% BARK子带参数表
Fk=[50 20 100; 150 100 200; 250 200 300; 350 300 400; 450 400 510; 570 510 630; 700 630 770;...
840 770 920; 1000 920 1080; 1170 1080 1270; 1370 1270 1480; 1600 1480 1720; 1850 1720 2000;...
2150 2000 2320; 2500 2320 2700; 2900 2700 3150; 3400 3150 3700; 4000 3700 4400;...
4800 4400 5300; 5800 5300 6400; 7000 6400 7700; 8500 7700 9500; 10500 9500 12000;...
13500 12000 15500; 18775 15500 22050];
% 插值
fs2=fix(fs/2);
y=y';
for i=1:fn
sourfft(i,:)=fft(y(i,:),wlen); % FFT变换
sourfft1(i,:)=abs(sourfft(i,1:wlen/2)); % 取正频率幅值
sourre(i,:)=resample(sourfft1(i,:),fs2,wlen/2); % 谱线内插
end
% 计算BARK滤波器个数
for k=1 : 25
if Fk(k,3)>fs2
break
end
end
num=k-1;
for i=1 : fn
Sr=sourre(i,:); % 取一帧谱值
for k=1 : num
m1=Fk(k,2); m2=Fk(k,3); % 求出BARK滤波器的上下截止频率
Srt=Sr(m1:m2); % 取来相应的谱线
Dst(k)=var(Srt); % 求笫k个BARK滤波器中的方差值
end
Dvar(i)=mean(Dst); % 求各个BARK滤波器中方差值的平均值
end
Dvarm=multimidfilter(Dvar,10); % 平滑处理
dth=mean(Dvarm(1:(NIS))); % 阈值计算
T1=1.5*dth;
T2=3*dth;
[voiceseg,vsl,SF,NF]=vad_param1D(Dvarm,T1,T2); % BARK子带的频带方差双门限的端点检测
% 作图
subplot 311; plot(time,x,'k');
title('纯语音波形');
ylabel('幅值'); axis([0 max(time) -1 1]);
subplot 312; plot(time,signal,'k');
title('加噪语音波形(信噪比10dB)');
ylabel('幅值'); axis([0 max(time) -1 1]);
subplot 313; plot(frameTime,Dvar,'k');
title('短时BARK子带分离的频带方差值'); axis([0 max(time) 0 1.2*max(Dvar)]);
xlabel('时间/s'); ylabel('幅值');
line([0,frameTime(fn)], [T1 T1], 'color','k','LineStyle','--');
line([0,frameTime(fn)], [T2 T2], 'color','k','LineStyle','-');
for k=1 : vsl % 标出语音端点
nx1=voiceseg(k).begin; nx2=voiceseg(k).end;
fprintf('%4d %4d %4d\n',k,nx1,nx2);
subplot 311;
line([frameTime(nx1) frameTime(nx1)],[-1 1],'color','k','LineStyle','-');
line([frameTime(nx2) frameTime(nx2)],[-1 1],'color','k','LineStyle','--');
subplot 313;
line([frameTime(nx1) frameTime(nx1)],[0 1.2*max(Dvar)],'color','k','LineStyle','-');
line([frameTime(nx2) frameTime(nx2)],[0 1.2*max(Dvar)],'color','k','LineStyle','--');
end
%
% pr6_4_4
clear all; clc; close all;
run Set_I % 基本设置
run PART_I % 读入数据,分帧等准备
h=waitbar(0,'Running...'); % 设置运行程序进度条图,初始化
set(h,'name','端点检测 - 0%'); % 设置本图的名称"端点检测"
for i=1 : fn
u=y(:,i); % 取第i帧数据
v=wavlet_barkms(u,'db2',fs); % 利用小波包分解获取17个BARK子带数据
num=size(v,1);
for k=1 : num
Srt=v(k,:); % 取得第k个BARK子带中的数据
Dst(k)=var(Srt); % 求第k个BARK子带中的方差值
end
Dvar(i)=mean(Dst); % 对17个BARK子带计算方差平均
waitbar(i/fn,h) % 显示运行的百分比,用红条表示
% 显示本图的名称"端点检测",并显示运行的百分比数,用数字表示
set(h,'name',['端点检测 - ' sprintf('%2.1f',i/fn*100) '%'])
end
close(h) % 关闭程序进度条图
Dvarm=multimidfilter(Dvar,10); % 平滑处理
Dvarm=Dvarm/max(Dvarm); % 幅值归一化
dth=mean(Dvarm(1:(NIS))); % 阈值计算
T1=1.5*dth;
T2=2.5*dth;
[voiceseg,vsl,SF,NF]=vad_param1D(Dvarm,T1,T2);% 小波包BARK子带时域方差双门限的端点检测
% 作图
subplot 311; plot(time,x,'k');
title('纯语音波形');
ylabel('幅值'); axis([0 max(time) -1 1]);
subplot 312; plot(time,signal,'k');
title('加噪语音波形(信噪比10dB)');
ylabel('幅值'); axis([0 max(time) -1 1]);
subplot 313; plot(frameTime,Dvarm,'k');
title('小波包短时BARK子带方差值'); axis([0 max(time) 0 1.2*max(Dvarm)]);
xlabel('时间/s'); ylabel('幅值');
line([0,frameTime(fn)], [T1 T1], 'color','k','LineStyle','--');
line([0,frameTime(fn)], [T2 T2], 'color','k','LineStyle','-');
for k=1 : vsl % 标出语音端点
nx1=voiceseg(k).begin; nx2=voiceseg(k).end;
fprintf('%4d %4d %4d\n',k,nx1,nx2);
subplot 311;
line([frameTime(nx1) frameTime(nx1)],[-1 1],'color','k','LineStyle','-');
line([frameTime(nx2) frameTime(nx2)],[-1 1],'color','k','LineStyle','--');
subplot 313;
line([frameTime(nx1) frameTime(nx1)],[0 1.2*max(Dvarm)],'color','k','LineStyle','-');
line([frameTime(nx2) frameTime(nx2)],[0 1.2*max(Dvarm)],'color','k','LineStyle','--');
end
function y=multimidfilter(x,m)
a=x;
for k=1 : m
b=medfilt1(a, 5);
a=b;
end
y=b;
语音信号的端点检测即检测出有效语音段,排除非有效语音的干扰,从而实现更好的语音识别效果。本章从最基础的双门限检测,到双门限检测法的改进方法,相关法、方差法、谱距离法、谱熵法、能零比方法、能熵比方法、小波变换方法、EMD分解方法,最后对低信噪比时的语音信号也进行了一定的讨论与研究。
语音信号的端点检测属于语音信号进入下一步语音识别的基础,对本章内容感兴趣或者想充分学习了解的,建议去研习书中第六章节的内容。后期会对其中一些知识点在自己理解的基础上进行讨论补充,欢迎大家一起学习交流。
关于宋老师:宋知用——默默传授MATLAB与信号处理知识的老人家
本系列文章列表如下:
《MATLAB语音信号分析与合成(第二版)》:第2章 语音信号的时域、频域特性和短时分析技术
《MATLAB语音信号分析与合成(第二版)》:第3章 语音信号在其他变换域中的分析技术和特性
《MATLAB语音信号分析与合成(第二版)》:第4章 语音信号的线性预测分析
《MATLAB语音信号分析与合成(第二版)》:第5章 带噪语音和预处理
《MATLAB语音信号分析与合成(第二版)》:第6章 语音端点的检测(1)
《MATLAB语音信号分析与合成(第二版)》:第6章 语音端点的检测(2)
《MATLAB语音信号分析与合成(第二版)》:第7章 语音信号的减噪
《MATLAB语音信号分析与合成(第二版)》:第8章 基音周期的估算方法
《MATLAB语音信号分析与合成(第二版)》:第9章 共振峰的估算方法
《MATLAB语音信号分析与合成(第二版)》:第10章 语音信号的合成算法