语音识别之VAD——静默检测

1、简介

          静默检测对于语音识别有这很重要作用,什么是静默检测?顾名思义就是检测语音的状态,静默状态还是**状态,这样才能保证送进语音识别模型的是一句完整语音数据,排除一些噪音的干扰。如同下图所示,当然这里存在一个问题,就是多长时间的静默状态才当作语音的结束,以及多大的语音能量以及多长时间的状态持续才当作语音的开始。

语音识别之VAD——静默检测

 

2、算法简介

         2.1 语音**状态检测

               一般情况下,麦克风录入的音量肯定存在或多或少的噪音,例如撞击声、敲击声等,一般需要排除的噪音是端、快类型,防止造成检测为误**状态。噪音具有时效性,也就是说不同时间不同地点的噪音肯定都不同,所以这里就需要对噪音有一个取样评判的过程。

               语音在短时间内都是为平稳状态,一般10到30ms,也就是这一段时间的语音状态近乎一致。故对录入的音频数据要有一个分帧的过程,当然一般情况下也需要帧移,例如:每次录入30ms,但是我每次只取20ms的音频,为保证每次处理的音频数据与上次的变化不会过于跳变,所以下次处理的数据包含上次数据的后10ms。

               |————10ms——————|—————10ms—————|—————10ms—————|

               |<------------------------第一次数据处理--------------------------->|

                                                              |<------------------------第二次数据处理--------------------------->|

            每次处理数据的最小单位为一帧,也就是上图所说的状态,当然对于一般情况就取10ms一帧不进行帧移也可以。首先获取背景噪音的能量,每帧的能量可以取平方均值,可以取前n帧作为背景噪音的能量值,噪音的能量值需要设置一个最小门限,防止出现很安静的状态下误处理。仅仅比较当前的语音能量与噪音能量,显然不够,还需要监控语音的变化状态,是否跳动以及此状态的维持时间。当这几个条件同时满足方可判断为**状态。常用方法:过零检测与门限控制。

例程如下:

//能量计算

float CalculateEnergy(const float *frames, size_t frames_count)

{
    float energy = 0;
    for (size_t i = 0; i < frames_count; i++) {
        energy += frames[i] * frames[i];
    }

    return energy / (float)frames_count;
}

//过零检测

short CalculateZeroCross(const short *frame, size_t frames_count) {
    short zero_cross_cnt = 0;
    short cur_status = 0; //本次状态
    short last_status = 0; //上次状态
    for (size_t i = 0; i < frames_count; i++) //循环判断一帧里的所有数据
    {
        cur_status = (frame[i] > 0) ? 1 : -1;

        if ((last_status != 0) && (cur_status != last_status)) {
            zero_cross_cnt++;
        }
        last_status = cur_status;
    }

    return zero_cross_cnt;           //返回跳动次数
}

每次判断为**状态后,需要记录此时的时间或者帧数,作为之后的结束判断。

      2.1 语音结束状态检测

            语音结束判断较为简单,只要判断目前的时间与上次的**时间差值,大于一定阈值,即可判断为语句结束。

3、总结

       以上的VAD检测方法适用于要求不高的情况,一般噪音状态检测效果还不错,如果需要更高要求的VAD检测,就不能仅局限于时域的特征,还需要再频域上进行判断各自带的一些特征,可以参考webrtc的vad检测。