语音信号的端点检测的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);

下面是批处理的代码:

点赞