这个是利用子带谱熵法进行语音信号端点检测的代码,给出了参考文献,有兴趣时可以看一下,发博客以作备忘!!!
function [voiceseg,vsl]=vad_1(s,fs)
%该函数采用子带谱熵法检测语音信号的端点
%输入:s :输入信号s
% fs:输入信号的采样频率(Hz)
% Is:设定一个前导无话段用来计算门限值(比如我们分段时预留了至少0.5s,
% 故可以设定Is=0.5,或者根据实际情况取),若分段时没预留噪声,或者
% 预留噪声段混有语音,会对该算法造成较大影响。
%输出:voiceseg 为一个数组,分别给出了起始帧结束帧和有话段帧数
%说明:1.这里我们认为语音段必须大于0.2s,噪声段必须大于0.1s
% 2.调整门限值(T1,T2)或 maxsilence,minlen或者K值可以调整端点检测效果
%参考文献:一种基于自适应谱熵的端点检测改进方法 1006—9348(2010)12—0373—03
% 20150617 by boat
%检查人:
%基本参数和变量的设定和求取
IS=0.3;
wlen=floor(25/1000*fs); % 帧长设为25ms
inc=floor(wlen/2); % 帧移为帧长的50%
xx1=s-mean(s); % 去除直流分量
x=xx1/max(abs(xx1)); % 对幅值归一化
win=hamming(wlen); % 设定窗函数
y=enframe(x,win,inc)’; % 分帧并转置,转置后每一列为一帧
nframe=size(y,2); % 求帧数
NIS=fix((IS*fs-wlen)/inc +1); % 求前导无话段帧数
df=fs/wlen; % 求出FFT后频率分辨率
fx1=fix(80/df)+1;
fx2=fix(4500/df)+1; % 找出80Hz和4500Hz的位置
km=floor(wlen/8); % 计算出子带个数,每个子带包含4条谱线
K=4.8; % 设定常数K,K值对端点检测效果有影响
Eb=zeros(1,km);
Hb=zeros(1,nframe);
for i=1:nframe
A=abs(fft(y(:,i))); % 对一帧进行快速傅里叶变换并取幅值
E=zeros(wlen/2+1,1);
E(fx1+1:fx2-1)=A(fx1+1:fx2-1); % 取80~4500Hz之间的分量
E=E.*E; % 计算能量
P1=E/sum(E); % 归一化
index=find(P1>=0.7); % 剔除P1>0.9的分量
if ~isempty(index)
E(index)=0;
end
for m=1:km % 计算子带能量
Eb(m)=sum(E(4*m-3:4*m));
end
prob=(Eb+K)/sum(Eb+K); % 计算子带概率
Hb(i) = -sum(prob.*log(prob+eps)); % 计算子带谱熵
end
% 多次中值滤波做平滑处理
a=Hb;
for kk=1:10
b=medfilt1(a,5);
a=b;
end
Enm=b ;
% 设置阈值
Me=min(Enm);
eth=mean(Enm(1:NIS));
Det=eth-Me;
T1=0.9402*Det+Me;
T2=0.935*Det+Me;
%初始化
nframe=length(Enm); % 取得帧数
maxsilence = 4; % 静音长度不够0.1s的认为语音没有停止
minlen = 8; %语音长度不够0.2s的认为是噪声
status = 0;
count = 0;
silence = 0;
%开始端点检测
%检测方法为单参数双门限法
xn=1;
for n=2:nframe
switch status
case {0,1} % 0 = 静音, 1 = 可能开始
if Enm(n) < T2 % 确信进入语音段
x1(xn) = max(n-count(xn)-1,1);
status = 2;
silence(xn) = 0;
count(xn) = count(xn) + 1;
elseif Enm(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 Enm(n) < T1 % 保持在语音段
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
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)=nframe;
end
SF=zeros(1,nframe); % 按x1和x2,对SF和NF赋值
for i=1 : el
SF(x1(i):x2(i))=1;
end
speechIndex=find(SF==1); % 计算voiceseg
if speechIndex(1)==0
voicedIndex=find(speechIndex); % 寻找express中为1的位置
else
voicedIndex=speechIndex;
end
voiceseg = [];
k = 1;
voiceseg(k).begin = voicedIndex(1); % 设置第一组有话段的起始位置
for i=1:length(voicedIndex)-1,
if voicedIndex(i+1)-voicedIndex(i)>1, % 本组有话段结束
voiceseg(k).end = voicedIndex(i); % 设置本组有话段的结束位置
voiceseg(k+1).begin = voicedIndex(i+1);% 设置下一组有话段的起始位置
k = k+1;
end
end
voiceseg(k).end = voicedIndex(end); % 最后一组有话段的结束位置
% 计算每组有话段的长度
for i=1 :k
voiceseg(i).duration=voiceseg(i).end-voiceseg(i).begin+1;
end
vsl=length(voiceseg);
下面是批处理的代码: