语音信号的端点检测的matlab代码(子带谱熵法法)

这个是利用子带谱熵法进行语音信号端点检测的代码,给出了参考文献,有兴趣时可以看一下,发博客以作备忘!!!

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);



下面是批处理的代码:


你可能感兴趣的:(matlab,语音,子带谱熵法,端点检测)