WebRTC VAD流程解析

语音活动检测算法大致可以分为三类,第一类就是最简单的基于阈值的判别方法,这个以前讲过了,参考语音活动检测;第二类是WebRTC使用的基于GMM的检测方法;第三类就是基于深度学习的检测方法,这个也讲过了,参考使用LSTM进行端点检测。废话不多说,开始进入正题。

1. Introduction

WebRTC VAD支持8/16/24/32/48kHz采样率,不过都会重采样到8kHz进行计算,每一帧长度可以为80/10ms、160/20ms和240/30ms三种。VAD具有如下的四种模式,分别表示通用模式、低比特率模式、激进模式和非常激进模式,在不同模式下高斯混合模型的参数和判决的门限值有所不同。

    enum Aggressiveness {
        kVadNormal = 0,
        kVadLowBitrate = 1,
        kVadAggressive = 2,
        kVadVeryAggressive = 3
    };
复制代码

WebRTC采用GMM统计模型对语音进行VAD判决,将04kHz划分为如下的六个频段:80250Hz,250500Hz,5001kHz,1k2kHz,2k3kHz,3k~4kHz,并使用这些频段的子带能量作为GMM相关特征。

II. Initialization

Talk is cheap, 直接看代码,WebRtcVad_InitCore 函数对以下内容进行初始化。

  • VAD初始状态,这里将VAD初始状态设为语音存在,这会导致对于某些样本开始的一段语音VAD检测结果均为1。

  • Hang-over的相关参数。

  • 下采样滤波器的系数,前面讲过不管输入的采样率是多少,最后都会下采样到8kHz进行处理。

  • GMM的语音噪声的均值和方差,这里kTableSize=12表示两个模型各6个子带,定标Q=7。

  • 最小值向量,用来跟踪噪声。

  • split filter系数,WebRTC对语音进行子带划分是通过split filter进行处理的而非FFT。

  • 检测模式默认为aggressiveness,后面可通过WebRtcVad_set_mode函数进行修改。

    int WebRtcVad_InitCore(VadInstT *self) { int i;

    if (self == NULL) {
        return -1;
    }
    
    // Initialization of general struct variables.
    self->vad = 1;  // Speech active (=1).
    self->frame_counter = 0;
    self->over_hang = 0;
    self->num_of_speech = 0;
    
    // Initialization of downsampling filter state.
    memset(self->downsampling_filter_states, 0,
           sizeof(self->downsampling_filter_states));
    
    // Initialization of 48 to 8 kHz downsampling.
    WebRtcSpl_ResetResample48khzTo8khz(&self->state_48_to_8);
    
    // Read initial PDF parameters.
    for (i = 0; i < kTableSize; i++) {
        self->noise_means[i] = kNoiseDataMeans[i];
        self->speech_means[i] = kSpeechDataMeans[i];
        self->noise_stds[i] = kNoiseDataStds[i];
        self->speech_stds[i] = kSpeechDataStds[i];
    }
    
    // Initialize Index and Minimum value vectors.
    for (i = 0; i < 16 * kNumChannels; i++) {
        self->low_value_vector[i] = 10000;
        self->index_vector[i] = 0;
    }
    
    // Initialize splitting filter states.
    memset(self->upper_state, 0, sizeof(self->upper_state));
    memset(self->lower_state, 0, sizeof(self->lower_state));
    
    // Initialize high pass filter states.
    memset(self->hp_filter_state, 0, sizeof(self->hp_filter_state));
    
    // Initialize mean value memory, for WebRtcVad_FindMinimum().
    for (i = 0; i < kNumChannels; i++) {
        self->mean_value[i] = 1600;
    }
    
    // Set aggressiveness mode to default (=|kDefaultMode|).
    if (WebRtcVad_set_mode_core(self, kDefaultMode) != 0) {
        return -1;
    }
    
    self->init_flag = kInitCheck;
    
    return 0;
    复制代码

    }

III. VAD Decision

下面开始介绍WebRTC的VAD处理流程(WebRtcVad_Process),具体的步骤如下所示:

  1. 进行一些基本的检测(WebRtcVad_ValidRateAndFrameLength),检测VAD结构体是否被初始化,语音帧长度是否满足条件。

  2. 对语音进行下采样,采样到8kHz,WebRTC的下采样并不是一步到位。以48kHz下采样到8kHz为例(WebRtcSpl_Resample48khzTo8khz):首先48kHz下采样到24kHz,然后对24kHz的语音数据进行低通滤波(这一步并不改变采样率),后面继续进行下采样24kHz->16kHz,16kHz->8kHz,最终得到8kHz的语音数据。

  3. 得到8kHz的语音数据之后,我们计算语音各个子带的能量作为GMM相关特征(WebRtcVad_CalculateFeatures)。首先将4kHz的数据分为02kHz和2k4kHz,然后将2k4kHz部分划分为2k3kHz和3k4kHz两部分。02kHz中则先分为01kHz和1k2kHz两部分,其中01kHz再分为0250Hz和250500Hz,最后对0250Hz部分进行80Hz高通滤波得到80250Hz的部分,至此得到六个子带:80250Hz,250500Hz,5001kHz,1k2kHz,2k3kHz,3k~4kHz六个子带,分别计算这六个子带的对数能量作为GMM相关特征。除此之外,还计算了一个total_energy,这个参数会在后面计算GMM的时候使用(WebRtcVad_GmmProbability)。

  4. 接下来就是计算GMM的部分的,关于GMM的原理可以参考从零实现机器学习算法(十九)高斯混合模型。在计算前会根据语音帧长度选择不同的判决阈值。

  5. 首先判断上一步骤计算得到的total_energy是否大于能量的门限值kMinEnergy,如果大于则对当前帧进行处理;否则直接将vad_flag置为0。

  6. 计算每个子带对应的高斯概率(WebRtcVad_GaussianProbability)并与子带的权重相乘作为语音/噪声最终的概率,这里WebRTC为了简化计算,假设语音和噪声的高斯模型是不相关的。

  7. 计算每个子带的对数似然比(log likelihood ratio, LLR),每个子带的似然比会和阈值进行比较作为一个局部的VAD判决。所有子带的对数加权似然比之和和阈值比较作一个全局的VAD判决。当局部判决或者全局判决结果有一个判决有语音时则认定当前帧是语音帧。

  8. 使用hangover对结果进行平滑

IV. Updation

WebRTC的VAD具有自适应的能力是因为其做完VAD判决后会更新GMM的参数。

  1. 计算局部(每个子带)语音和噪声的概率用于更新GMM参数。

  2. 跟踪每个子带特征的最小值(WebRtcVad_FindMinimum), 函数对每个特征求出了100个帧里头的16个最小值。这些最小值都有一个age,最大不超过100,如果当前特征是100 帧里 16 个最小值之一,那么计算并返回五个最小值的中位数。这里得到的最小值后面会用来更新噪声。

  3. 更新GMM参数即语音/噪声的均值和方差,其中噪声的均值只在当前语音帧为非语音帧时进行更新。

  4. 当语音高斯模型和噪声高斯模型很相似时,分离它们。

V. Conclusion

WebRTC的VAD在信噪比较高的情况下可以得到比较好的结果,随着信噪比的下降,其检测结果也随之恶化。此外,WebRTC的VAD的工程实现使用了定点数运算和一些近似运算这使得其资源占用不是很高,但使得代码比较难读。本文只解析了WebRTC中VAD大致的流程,其中一些具体的细节还有待读者自己去深究。WebRTC VAD的内容还是很蛮多的,如有所遗漏敬请见谅。

本文相关代码,关注公众号语音算法组,菜单栏点击Code获取。

参考文献:

[1]. 实时语音处理实践指南

[2]. A Statistical Model-Based Voice Activity Detection

猜你喜欢

转载自juejin.im/post/7031919671284596750