根据对双门限的理解和修改,重写了一个离线版端点检测。
function [st,en]=VAD(x, fs)
x=double(x);
x=x/max(abs(x));
framelen= floor(fs*40/1000);%
frameinc= floor(fs*10/1000);%
y=enframe(x,framelen,frameinc);
%计算短时间能量
amp=sum(abs(y),2);
%开始端点检测
tmp1=enframe(x(1:length(x)-1),framelen,frameinc);
tmp2=enframe(x(2:length(x)),framelen,frameinc);
signs=(tmp1.*tmp2)<0; %
diffs=(tmp1-tmp2)>0.01;
zcr=sum(signs.*diffs,2); %
zcr=[zcr;zcr(end)];
zcr_yu=0.2*mean(zcr);%
yuzhi=0.2*mean(amp);%
minspeech=10;
count=0;%
start=[];
tail=[];
N=length(amp);
flag=0;%
biaozhi2=0;
biaozhi3=0;
%%%%%%%%%%%%
for n=1:N
if amp(n)<yuzhi || (zcr(n)<zcr_yu)
continue;
end
kaitou=n;
break;
end
for n=N:-1:1
if amp(n)<yuzhi || (zcr(n)<zcr_yu)
continue;
end
jiewei=n;
break;
end
noise=[amp(1:kaitou-1);amp(jiewei+1:end)];
noise_mean=mean(noise);
noise_var = std(noise);
speech_mean=mean(amp(kaitou:jiewei));%noise_mean
yuzhi1= 0.3*speech_mean; %
yuzhi2= max(0.3*speech_mean , (noise_mean + noise_var)*1.3);%
noise_zcr=[zcr(1:kaitou-1);zcr(jiewei+1:end)];
noise_zcr_mean = mean(noise_zcr);
noise_zcr_std = std(noise_zcr);
speech_zcr_mean=mean(zcr(kaitou:jiewei));
zcr_yu1 = 0.3*speech_zcr_mean;
zcr_yu2 = max(0.3*speech_zcr_mean , (noise_zcr_mean+noise_zcr_std)*0.3);
%%%%%%%%%%%%%%
st = [];
en = [];
bstart_state = 0;
bend_state = 0;
segment = 0;
unvoice = 0;
voice_min_len = 7;% 最短语音长度70ms
unvoice_min_len = 5;%结束段最小持续50ms
st_candicate = 0;
en_candicate = 0;
for i = 1:N
if( (amp(i) >= yuzhi2 && zcr(i) >= zcr_yu1) && ~bstart_state ) %find start
bstart_state = 1; %
if(~st_candicate)
st_candicate = i;
end
segment = segment + 1;
elseif( (amp(i) >= yuzhi2 || zcr(i) >= zcr_yu1) && bstart_state )%
if(unvoice >= unvoice_min_len) %
st = [st; st_candicate];
en_candicate = en_candicate + unvoice_min_len - 1;
en = [en; en_candicate];
bstart_state = 0;
bend_state = 1;
unvoice = 0;
segment = 0;
st_candicate = 0;
else %
unvoice = 0; %
bend_state = 0;
segment = segment + 1;
end
elseif( (amp(i) < yuzhi2 && zcr(i) < zcr_yu1) && bstart_state ) %
if segment >= voice_min_len %
unvoice = unvoice + 1; %
if ~bend_state %
en_candicate = i;
end
bend_state = 1; %
else %
bstart_state = 0; %
segment = 0;
bend_state = 0;
unvoice = 0;
st_candicate = 0;
%prepare_for_start = 0;
en_candicate = 0;
end
elseif((amp(i) >= yuzhi2 || zcr(i) >= zcr_yu1) && ~bstart_state) %
if ~ st_candicate
st_candicate = i;
end
else%
st_candicate = 0;
continue;
end
end
if(unvoice >= unvoice_min_len )%
st = [st; st_candicate];
en = [en; (en_candicate + unvoice_min_len -1)];
segment = 0;
end
if( segment >= voice_min_len) %
st = [st; st_candicate];
en = [en; N];
end
figure(1);
subplot(3,1,1)
plot(x); %原始语音波形
axis([1,length(x),-1,1])
ylabel('speech');
xlabel('样本点');
for k=1:length(st)
line([st(k)*frameinc,st(k)*frameinc],[-1,1],'linestyle',':','color','blue','LineWidth',2);
line([en(k)*frameinc,en(k)*frameinc],[-1,1],'linestyle',':','color','red','LineWidth',2);
end
subplot(3,1,2)
plot(amp);%原始语音能量
axis([1,length(amp),0,max(amp)])
ylabel('energy');
xlabel('帧数');
line([1,N],[yuzhi1,yuzhi1],'color','yellow','LineWidth',2);%由语音能量
line([1,N],[yuzhi2,yuzhi2],'color','red','LineWidth',2);%由噪声平均能量和语音能量比较而得
for k=1:length(st)
line([st(k),st(k)],[min(amp),max(amp)],'linestyle',':','color','blue','LineWidth',2);
line([en(k),en(k)],[min(amp),max(amp)],'linestyle',':','color','red','LineWidth',2);
end
subplot(3,1,3)
plot(zcr);%原始语音过零率
axis([1,length(zcr),0,max(zcr)])
ylabel('zcr');
xlabel('帧数');
line([1,N],[zcr_yu1,zcr_yu1],'color','yellow','LineWidth',2);%由语音能量
line([1,N],[zcr_yu2,zcr_yu2],'color','red','LineWidth',2);%语音加噪音
for k=1:length(st)
line([st(k),st(k)],[min(zcr),max(zcr)],'linestyle',':','color','blue','LineWidth',2);
line([en(k),en(k)],[min(zcr),max(zcr)],'linestyle',':','color','red','LineWidth',2);
end
end
用语音识别的强制对齐自动切分连续语音汉字,工程量和资源巨大,此处做了个简易版的汉字自动切分,效果还可以。