webrtc之語音活動下——VAD人聲判定原理以及源碼詳解

文章目錄

  • 前言
  • 一、高斯混合模型介紹
    • 1.高斯模型舉例
      • 1)定義
      • 2)舉例說明
    • 2.高斯混合模型(GMM)
      • 1)定義
      • 2)舉例說明
      • 3)一維曲線
  • 二、VAD高斯混合模型
    • 1.模型訓練介紹
      • 1)訓練方法
      • 2)訓練結果
    • 2.噪聲高斯模型分布
      • 1)matlab代碼
      • 2)結果
    • 2.人聲高斯模型分布
  • 三、VAD人聲判斷
    • 1.高斯分布計算
      • 1)公式
      • 2)對應代碼
    • 2.人聲判定策略
      • 1)判斷機制
      • 2)重要閾值確認
      • 3)整體流程
        • 第一步:計算噪聲/人聲概率分布
        • 第二步:噪聲/人聲比較
        • 第三步:判斷單個閾值
        • 第四步:全局判定
  • 四、模型更新
    • 1.計算條件概率
    • 2.獲取噪聲基線和均值
      • 1)噪聲基線的作用
      • 2)確定噪聲基線
      • 3)獲取噪聲均值
    • 3.更新噪聲均值
      • 1)短時更新
      • 2)平滑修正
      • 3)邊界約束
      • 4)策略意義
    • 4.更新人聲均值
    • 5.更新人聲方差
      • 1)更新公式
      • 2)代碼解析
      • 3)邊界約束
    • 6.更新噪聲方差
    • 7.模型分離
      • 1)計算全局均值
      • 2)計算差值
      • 3)如果模型太近,強行拉開
      • 4)限制模型漂移范圍
  • 五、hangover 機制
    • 1.策略作用
    • 2.整體流程
    • 3.對應代碼
      • 1)非語音處理
      • 2)語音
  • 總結


前言

在上一篇文章中,介紹了VAD對于能量的計算原理和策略介紹。本篇文章中講進一步介紹VAD中的人聲/噪聲決策,這是基于高斯混合模型,也就是GMM進行計算的。

本篇文章將會對照源碼、結合圖像進行介紹:

  • GMM模型介紹(可以跳過)
  • webrtc中VAD的高斯模型(包括來源以及使用)
  • 人聲/噪聲決策以及在線更新機制
  • hangover機制

本篇文章比較長,可以耐心觀看。

|版本聲明:山河君,未經博主允許,禁止轉載


一、高斯混合模型介紹

如果對于高斯模型很熟悉那么可以跳過這一節,這里需要明白的是概率密度、區間概率和條件概率的區別。

1.高斯模型舉例

1)定義

高斯分布(又叫正態分布)是一種常見的概率分布,它的概率密度也叫做似然函數(PDF)是:
p(x)=12πσ2exp?(?(x?μ)22σ2)p(x)=\frac{1}{\sqrt{2\pi\sigma^2}}\exp\Big(-\frac{(x-\mu)^2}{2\sigma^2}\Big)p(x)=2πσ2?1?exp(?2σ2(x?μ)2?)

  • μ\muμ為均值:分布的中心位置,數據大部分集中在它附近。
  • σ2\sigma^2σ2表示方差:分布的寬度,數值越大,圖像分布越平;數值越小,分布越尖。

而使用高斯分布計算區間概率公式為:
P(a<X<b)=∫abp(x)dxP(a<X<b)=\int_a^bp(x)dxP(a<X<b)=ab?p(x)dx

2)舉例說明

假設有一門數學考試,100 分滿分,有5個學生的成績分別是60,70,80,75,6560,70,80,75,6560,70,80,75,65

  • 均值μ\muμμ=1N∑i=1Nxi=60+70+80+75+655=70\mu=\frac{1}{N}\sum_{i=1}^{N}x_i = \frac{60+70+80+75+65}{5}=70μ=N1?i=1N?xi?=560+70+80+75+65?=70
  • 方差σ2=1N∑i=1N(xi?μ)2=(?10)2+02+102+52+(?5)25=50\sigma^2=\frac{1}{N}\sum_{i=1}^{N}(x_i-\mu)^2=\frac{(-10)^2+0^2+10^2+5^2+(-5)^2}{5}=50σ2=N1?i=1N?(xi??μ)2=5(?10)2+02+102+52+(?5)2?=50

那么現在還有一個學生

  • 他的成績為737373分的概率帶入公式最終結果為:5.16%5.16\%5.16%
  • 他的成績在[70,75][70,75][70,75]分之間的概率帶入公式為:26%26\%26%

2.高斯混合模型(GMM)

1)定義

GMM 就是多個高斯分布的加權和,它的概率密度公式是:
p(x)=∑k=1Kwk?N(x∣uk,σk2)p(x)=\sum_{k=1}^{K}w_k \cdot \Nu(x|u_k,\sigma_k^2)p(x)=k=1K?wk??N(xuk?,σk2?)

  • kkk:高斯分布的個數
  • wkw_kwk?:每個分布的權重(比例,所有權重相加 = 1)
  • uk,σku_k,\sigma_kuk?,σk?:第k 個高斯分布的均值和方差
  • N(x∣uk,σk2)\Nu(x|u_k,\sigma_k^2)N(xuk?,σk2?):第k個高斯分布的概率密度函數 (PDF)

而此時我們計算某一個值屬于哪一個高斯分布的過程叫做條件概率(責任度)
P(lk∣x)=lkp(x)=wk?N(x∣uk,σk2)p(x)P(l_k|x)=\frac{l_k}{p(x)}=\frac{w_k \cdot \Nu(x|u_k,\sigma_k^2)}{p(x)}P(lk?x)=p(x)lk??=p(x)wk??N(xuk?,σk2?)?

2)舉例說明

上文中是一個以成績的例子進行計算高斯分布,但顯然現實中要考慮的因素會更多:

  • 一部分學生是學霸30%30\%30%,集中在85±585 \pm 585±5分附近
  • 一部分學生是普通學生70%70\%70%,集中在65±1065\pm 1065±10分附近

那么由此可以得到:

  • 學霸群體:均值u1=85u_1 = 85u1?=85,標準差σ1=5\sigma_1= 5σ1?=5,權重w1=0.3w_1= 0.3w1?=0.3
  • 普通學生:均值u2=65u_2 = 65u2?=65,標準差σ2=10\sigma_2= 10σ2?=10,權重w2=0.7w_2= 0.7w2?=0.7

此時有一個學生成績為70,那么他的條件概率分別為:

  • P(l1∣x)=l1p(x)≈0.01067(1.07%)P(l_1|x)=\frac{l_1}{p(x)} \approx 0.01067(1.07\%)P(l1?x)=p(x)l1??0.01067(1.07%)
  • P(l2∣x)=l2p(x)=1?P(l1∣x)≈0.98933(98.93%)P(l_2|x)=\frac{l_2}{p(x)} = 1- P(l_1|x) \approx 0.98933 (98.93\%)P(l2?x)=p(x)l2??=1?P(l1?x)0.98933(98.93%)

很明顯,該學生大概率屬于普通學生

3)一維曲線

使用matlab畫出曲線

x = linspace(30, 100, 500);% 學霸群體參數
mu1 = 85; sigma1 = 5; w1 = 0.3;
pdf1 = w1 * normpdf(x, mu1, sigma1);% 普通學生群體參數
mu2 = 65; sigma2 = 10; w2 = 0.7;
pdf2 = w2 * normpdf(x, mu2, sigma2);% 混合分布
pdf_mix = pdf1 + pdf2;% 繪制
figure;
plot(x, pdf1, 'b--', 'LineWidth', 1.5); hold on;
plot(x, pdf2, 'g--', 'LineWidth', 1.5);
plot(x, pdf_mix, 'r-', 'LineWidth', 2);
grid on;
legend('學霸群體 N(85,5^2)*0.3', ...'普通學生 N(65,10^2)*0.7', ...'混合分布 (GMM)');
title('考試成績的高斯混合模型 (GMM)');
xlabel('成績');
ylabel('概率密度');

在這里插入圖片描述

二、VAD高斯混合模型

1.模型訓練介紹

1)訓練方法

webrtc關于VAD的高斯混合模型訓練是一種離線訓練,是通過以下幾個步驟得到的:

  • 準備數據
    • 收集了大量的語音數據(各種語言、男女聲、不同音量)
    • 收集了大量的噪聲數據(白噪聲、街道、辦公室、電話線路噪聲等)
  • 特征提取
    • 對每一幀音頻(10ms ~ 30ms)提取簡單特征:子帶能量、總能量、過零率
    • 特征維度很低,不像是MFCC會有多個維度,但適合在嵌入式環境運行
  • 擬合高斯分布
    • 把“語音幀特征”丟進一個高斯混合模型訓練器
    • 把“噪聲幀特征”丟進另一個 GMM 訓練器
    • 使用EM算法迭代得到每個分量的均值、方差、權重
  • 量化和存儲
    • 得到的均值、方差、權重定點數存儲(int16_t),方便在 C 代碼里運行
    • 該套參數固定寫死在代碼表里,運行時雖然會根據實際情況修改,但不會在運行后進行存儲

由此我們也可以直覺看出來這套模型的明顯的優缺點:

特性優點缺點
計算效率高,適合實時
模型復雜度低,易實現表達能力有限
多模態表示可以區分靜音/語音只適合簡單多模態
魯棒性可自適應噪聲變化對異常噪聲敏感
時序建模需要額外平滑

2)訓練結果

webrtc中訓練后的結果是以Q7格式分別存儲在:

  • kNoiseDataWeights:噪聲的兩個高斯分布權重,表格中的G0和G1
  • kNoiseDataMeans:噪聲的兩個高斯分布均值
  • kNoiseDataStds:噪聲的兩個高斯分布的方差
  • kSpeechDataWeights:噪聲的兩個高斯分布權重,表格中的G0和G1
  • kSpeechDataMeans:噪聲的兩個高斯分布均值
  • kSpeechDataStds:噪聲的兩個高斯分布的方差
  • G0:低幅度、常見特征,是頻帶特征里的主分量
  • G1:高幅度、變化性更大的特征,是頻帶特征里的補充分量
頻帶高斯Noise 權重Speech 權重Noise 均值Speech 均值Noise StdSpeech Std
0G0344867388306378555
G162824892100851064505
1G07245706510078493567
G16687671511823582524
2G05350677111843688585
G12547336963095931231
3G0948076469473474509
G1664638639571697828
4G05683782010879475492
G16241726675816881540
5G07578502081804211079
G11038143627483455850

在代碼中,將會在WebRtcVad_InitCore接口中存儲在VadInstT結構體中。

2.噪聲高斯模型分布

下面將根據具體的參數,畫出6個子帶的兩個高斯分布G0,G1圖像,然后再根據權重進行G0和G1加權后的高斯混合圖像。

1)matlab代碼

clc; clear; close all;% 原始 Q7 數據
kNoiseDataMeans_Q7   = [6738, 4892, 7065, 6715, 6771, 3369, 7646, 3863, 7820, 7266, 5020, 4362];
kNoiseDataStds_Q7    = [378, 1064, 493, 582, 688, 593, 474, 697, 475, 688, 421, 455];
kNoiseDataWeights_Q7 = [34, 62, 72, 66, 53, 25, 94, 66, 56, 62, 75, 103];% 轉為 Q0(浮點)
kNoiseDataMeans   = double(kNoiseDataMeans_Q7) / 128;
kNoiseDataStds    = double(kNoiseDataStds_Q7) / 128;
kNoiseDataWeights = double(kNoiseDataWeights_Q7);   % 權重本身可以直接使用kTableSize = length(kNoiseDataMeans);
numBands = kTableSize / 2;figure;for band = 1:numBands% 提取 G0, G1 的參數mean0 = kNoiseDataMeans(2*band-1);mean1 = kNoiseDataMeans(2*band);std0  = kNoiseDataStds(2*band-1);std1  = kNoiseDataStds(2*band);weight0 = kNoiseDataWeights(2*band-1);weight1 = kNoiseDataWeights(2*band);% x 范圍,取 ±4σx_min = min(mean0-4*std0, mean1-4*std1);x_max = max(mean0+4*std0, mean1+4*std1);x = linspace(x_min, x_max, 500);% 高斯分布G0 = (1/(std0*sqrt(2*pi))) * exp(-0.5*((x-mean0)/std0).^2);G1 = (1/(std1*sqrt(2*pi))) * exp(-0.5*((x-mean1)/std1).^2);% 加權混合Gmix = (weight0*G0 + weight1*G1) / (weight0 + weight1);% 繪圖subplot(2,3,band);plot(x, G0, 'b', 'LineWidth',1.5); hold on;plot(x, G1, 'r', 'LineWidth',1.5);plot(x, Gmix, 'k--', 'LineWidth',1.5);title(['Subband ' num2str(band)]);legend('G0','G1','G_{mix}');xlabel('Value'); ylabel('Probability Density');
end

2)結果

在這里插入圖片描述

2.人聲高斯模型分布

和計算噪聲高斯模型分布一樣,只需要將matlab中代碼替換即可,這里就不貼出代碼,直接看結果:
在這里插入圖片描述

三、VAD人聲判斷

在上一篇文章webrtc之語音活動上——VAD能量檢測原理以及源碼詳解中,我們已經知道了:

  • 檢測模式的區別
  • 幀長劃分方法
  • 6個非等寬頻帶的能量
  • 頻帶劃分的意義

1.高斯分布計算

1)公式

源碼中關于PDF計算使用Q格式運算以保證精度,其計算公式為:
1σ?exp?(?(x?m)22?σ2)\frac{1}{\sigma} \cdot \exp\Big(-\frac{(x - m)^2} { 2 * \sigma^2}\Big)σ1??exp(?2?σ2(x?m)2?)
該接口的計算方式少了一個2π\sqrt{2\pi}2π?,并且這里同樣將log?\loglog運算簡化,因此它是一個相對值(比例),而縮放后的 PDF是用于相對比較,不是嚴格的概率密度,不會影響后續決策與更新。

2)對應代碼

接口名為WebRtcVad_GaussianProbability,以下是參數和返回值:

  • input:頻帶能量,以Q4格式保存
  • mean:噪聲/人聲的高斯均值,Q7格式
  • std:噪聲/人聲的高斯方差,Q7格式
  • deltax?ms2\frac{x-m}{s^2}s2x?m?,用于后續更新模型,Q11格式
  • 返回值:概率密度,Q20格式

2.人聲判定策略

1)判斷機制

VAD判斷人聲是雙重機制:

  • 局部判定: 捕捉某個子帶特別強烈的語音
  • 全局判定:要求整體證據夠強
  • 最終決策:兩者取 OR,既保證靈敏度,又保證魯棒性

2)重要閾值確認

在設置模式WebRtcVad_set_mode_core接口里,確定

  • individual:單個頻帶人聲閾值范圍,用于局部判定
  • total:整體人聲閾值閾值范圍,用于整體

而對于不同幀長再進一步確定閾值,這里是在GmmProbability真正進行決策的地方進行確定。而對于:

  • 短幀10ms:由于信息少,則使用高閾值以避免誤判
  • 中等20ms:閾值低,放寬一些
  • 長幀30ms:本身信息穩定,為了避免太寬松 又設高

除此之外,在進行全局判定時,不同頻帶的權重存儲在kSpectrumWeight中。
在這里插入圖片描述
值得注意的是,這些閾值的由來是根據大規模實驗 + ROC 曲線分析 + 主觀聽感測試 手工調出來的經驗值。

3)整體流程

在這里插入圖片描述

第一步:計算噪聲/人聲概率分布

同一段頻帶語音根據噪聲/人聲的高斯參數,分別進行計算主分量/補充分量的概率分布,再根據權值計算真正的概率密度,代碼如下:

for (channel = 0; channel < kNumChannels; channel++) {h0_test = 0;h1_test = 0;for (k = 0; k < kNumGaussians; k++) {gaussian = channel + k * kNumChannels;tmp1_s32 = WebRtcVad_GaussianProbability(features[channel],self->noise_means[gaussian],self->noise_stds[gaussian],&deltaN[gaussian]);noise_probability[k] = kNoiseDataWeights[gaussian] * tmp1_s32;h0_test += noise_probability[k];  // Q27tmp1_s32 = WebRtcVad_GaussianProbability(features[channel],self->speech_means[gaussian],self->speech_stds[gaussian],&deltaS[gaussian]);speech_probability[k] = kSpeechDataWeights[gaussian] * tmp1_s32;h1_test += speech_probability[k];  // Q27}
第二步:噪聲/人聲比較

判斷單個頻帶是否為人聲使用的是似然對數比,也就是log?2(Pr(X∣H1)Pr(X∣H0))\log2(\frac{Pr(X|H1)}{Pr(X|H0)})log2(Pr(XH0)Pr(XH1)?),這么做的好處是:

  • 避免概率數值太小引起數值問題
  • LLR 的加和性質,可以進行多個頻帶綜合判定

值得注意的是:這近似在數幀平均下通常偏差較小,但對極端 / 較小樣本會產生偏差,所以后面通過局部+全局、hangover機制與模型更新來抵消誤判

這里的移位操作是為了簡化log?\loglog運算,用最高位表示為整數部分,用整數位的差值來近似log?2(h1/h0)\log2(h1/h0)log2(h1/h0),核心代碼為:

      shifts_h0 = WebRtcSpl_NormW32(h0_test);shifts_h1 = WebRtcSpl_NormW32(h1_test);if (h0_test == 0) {shifts_h0 = 31;}if (h1_test == 0) {shifts_h1 = 31;}log_likelihood_ratio = shifts_h0 - shifts_h1;
第三步:判斷單個閾值

如果單個頻帶的信號人聲強烈,將會直接認定為語音,值得注意的是即使判斷為人聲了,還是會接著計算其他頻帶,這是為了更新模型。核心代碼為:

if ((log_likelihood_ratio * 4) > individualTest) {vadflag = 1;}
第四步:全局判定

這里分為兩部分

  • 根據權重加合
  • 整體閾值判斷

其核心代碼如下:

sum_log_likelihood_ratios +=(int32_t) (log_likelihood_ratio * kSpectrumWeight[channel]);.......vadflag |= (sum_log_likelihood_ratios >= totalTest);         

四、模型更新

在進行模型更新時,需要考慮到平滑處理,所以這里運用到大量的平滑濾波的思想,見文章語音信號處理三十一——常用的時域/頻域平滑濾波。

1.計算條件概率

首先獲取各個頻帶噪聲/人聲在主分量和補充分量的條件概率,分別存儲在ngprvecsgprvec中。

如果h<0h<0h<0,那么默認該數值必然是主分量特征,以下是噪聲的條件概率核心代碼,人聲類似:

h0 = (int16_t) (h0_test >> 12);  // Q15if (h0 > 0) {tmp1_s32 = (noise_probability[0] & 0xFFFFF000) << 2;  // Q29ngprvec[channel] = (int16_t) WebRtcSpl_DivW32W16(tmp1_s32, h0);  // Q14ngprvec[channel + kNumChannels] = 16384 - ngprvec[channel];} else {ngprvec[channel] = 16384;}

2.獲取噪聲基線和均值

1)噪聲基線的作用

WebRTC VAD 和很多能量型 VAD 都是基于一個基本假設:在連續音頻信號中,語音的能量通常高于環境噪聲的能量。

而噪聲基的含義是指:環境噪聲在短時間或一段時間內的平均能量水平或特征值參考。

2)確定噪聲基線

噪聲基線的獲取是一種中值濾波的思想。在接口WebRtcVad_FindMinimum中,它的大致流程為:

輸入: feature_value, channel, self┌─? [1] 更新歷史值的年齡
│      ├─ 遍歷16個存儲的最小值
│      ├─ age < 100 → age++
│      └─ age == 100 → 移除該值,數組前移,最后位置填充大數
│
└─? [2] 判斷是否插入新值├─ feature_value 比數組中的某些值小│     └─ 找到插入位置 position│          └─ 從尾部往后移,插入新值,age=1└─ 否則丟棄(說明它不是最小16個之一)┌─? [3] 計算當前中位數
│      ├─ frame_counter > 2 → 取 smallest_values[2] (第3小)
│      └─ frame_counter <= 2 → 取 smallest_values[0] (最小值)
│
└─? [4] 平滑更新 mean_value[channel]├─ 如果 current_median < mean_value → α = 0.2 (快速下降)└─ 否則 α = 0.99 (緩慢上升)└─ mean_value[channel] ← α * mean_value[channel] + (1-α) * current_median[5] 返回 mean_value[channel]

這里可以對照代碼進行理解。

3)獲取噪聲均值

實現主要在WeightedAverage接口內,該接口的使用方法為:

  • 輸入主分量和補充分量的均值
  • 輸入對應的權重
  • 輸出計算后的均值

3.更新噪聲均值

1)短時更新

WebRTC 里不可能每次都存很多幀再做完整的EM,并且GMM更新中webrtc希望帶權,所以這里采用的是梯度遞推進行更新,公式為:
μnoisenow=μnoiseold+η?γnow(x?μnoiseold)\mu_{noise}^{now} = \mu_{noise}^{old}+\eta\cdot \gamma_{now}(x-\mu_{noise}^{old})μnoisenow?=μnoiseold?+η?γnow?(x?μnoiseold?)

  • η\etaη:權系數
  • γnow\gamma_{now}γnow?:當前頻帶當前分量的條件概率

由于篇幅原因,這里不做推導了,可以看看別的文章,或者可以私信博主

這里加權是并不希望噪聲平均變化過快。對應代碼為:

static const int16_t kNoiseUpdateConst = 655; // Q15
nmk2 = nmk;
if (!vadflag) {delt = (int16_t)((ngprvec[gaussian] * deltaN[gaussian]) >> 11);nmk2 = nmk + (int16_t)((delt * kNoiseUpdateConst) >> 22);
}

2)平滑修正

首先會用噪聲基線減去頻帶噪聲均值得到差值,再根據差值進行指數加權移動平均的思想,和當前幀的噪聲均值相加為下一幀的噪聲均值,對應代碼:

ndelt = (feature_minimum << 4) - tmp1_s16;
nmk3 = nmk2 + (int16_t)((ndelt * kBackEta) >> 9);

值得注意的是:平滑修正不受vadflag影響

3)邊界約束

即強制噪聲均值不能小于某個最小值,和防止噪聲均值過大,避免它被語音能量無限拉升。代碼如下:

tmp_s16 = (int16_t) ((k + 5) << 7);if (nmk3 < tmp_s16) {nmk3 = tmp_s16;}tmp_s16 = (int16_t) ((72 + k - channel) << 7);if (nmk3 > tmp_s16) {nmk3 = tmp_s16;}

4)策略意義

對于nmk2會受到γ\gammaγ的影響,而γ\gammaγ并不是固定常數,而是隨著當前幀的條件概率變動,一旦語音能量進入,更新方向就會發生錯誤。此時平滑修正的意義就體現出來:

  • 短時更新可能受到語音污染,可能偏離真實噪聲基線
  • 如果當前幀判定為非噪聲,那么均值會被kBackEta慢慢收斂回長期基線

4.更新人聲均值

和更新噪聲均值一樣的梯度更新計算方法,只是區別是:

  • 不需要進行長期拉回,因為人聲是特征動態明顯,不需要噪聲基線作為參考
  • 噪聲的限幅控制是為了保證模型穩定,避免漂移到語音區,而人聲限幅是跟隨語音分布變化,防止過度漂移,所以限幅范圍不同
  • 人聲均值向上取整而不是向下取整smk2 = smk + ((tmp_s16 + 1) >> 1);

5.更新人聲方差

1)更新公式

和上文一樣,這里采用的是遞推更新:
σnew=σold+η?wkσold((x?μold2)σold2?1)\sigma_{new}=\sigma_{old}+\eta\cdot \frac{w_k}{\sigma_{old}}\Big(\frac{(x-\mu_{old}^2)}{\sigma^2_{old}} -1\Big)σnew?=σold?+η?σold?wk??(σold2?(x?μold2?)??1)

2)代碼解析

tmp_s16 = ((smk + 4) >> 3);tmp_s16 = features[channel] - tmp_s16;  tmp1_s32 = (deltaS[gaussian] * tmp_s16) >> 3;tmp2_s32 = tmp1_s32 - 4096;tmp_s16 = sgprvec[gaussian] >> 2;tmp1_s32 = tmp_s16 * tmp2_s32;tmp2_s32 = tmp1_s32 >> 4; if (tmp2_s32 > 0) {tmp_s16 = (int16_t) WebRtcSpl_DivW32W16(tmp2_s32, ssk * 10);} else {tmp_s16 = (int16_t) WebRtcSpl_DivW32W16(-tmp2_s32, ssk * 10);tmp_s16 = -tmp_s16;}tmp_s16 += 128;  // Rounding.ssk += (tmp_s16 >> 8);

其中:

  • wkw_kwk?:來自于sgprvec
  • η\etaη:為0.025,代碼中體現在WebRtcSpl_DivW32W16產生了0.1的分母,sgprvec[gaussian] >> 2產生了4的分母
  • tmp_s16 = ((smk + 4) >> 3):這里的加4原因是為了保證右移3位是向上取整保存精度

3)邊界約束

方差值不能小于384

static const int16_t kMinStd = 384;
if (ssk < kMinStd) {ssk = kMinStd;}

6.更新噪聲方差

和更新人聲方差一樣,區別在于更新步長的大小是根據偏移量決定的,不過范圍大致在0.015~0.02 之間。

7.模型分離

為了防止speech GMM 模型和 noise GMM 模型的均值太接近而粘在一起,需要最終結合兩者進行決策。

1)計算全局均值

noise_global_mean  = WeightedAverage(&self->noise_means[channel], 0, &kNoiseDataWeights[channel]);
speech_global_mean = WeightedAverage(&self->speech_means[channel], 0, &kSpeechDataWeights[channel]);
  • 對每個 channel,把各個高斯分量的均值做加權平均。
  • 得到一個 全局 noise 平均值 和一個 全局 speech 平均值。

2)計算差值

diff = (int16_t)(speech_global_mean >> 9) - (int16_t)(noise_global_mean >> 9);
  • speech 全局均值 ? noise 全局均值。
  • 如果差值太小,說明兩個模型過于接近

3)如果模型太近,強行拉開

if (diff < kMinimumDifference[channel]) {tmp_s16 = kMinimumDifference[channel] - diff;// tmp1_s16 ≈ 0.8 * (缺口)// tmp2_s16 ≈ 0.2 * (缺口)// 把 speech 模型整體往上推一點speech_global_mean = WeightedAverage(&self->speech_means[channel],tmp1_s16, &kSpeechDataWeights[channel]);// 把 noise 模型整體往下拉一點noise_global_mean  = WeightedAverage(&self->noise_means[channel],-tmp2_s16, &kNoiseDataWeights[channel]);
}
  • 如果 gap 太小,就把 speech 往上推 80%,noise 往下拉 20%,強制保持至少 kMinimumDifference 的區分度。
  • 這樣避免兩個模型均值重疊,保持判決的魯棒性。

4)限制模型漂移范圍

if (speech_global_mean >> 7 > kMaximumSpeech[channel]) {// speech 上限...
}
if (noise_global_mean >> 7 > kMaximumNoise[channel]) {// noise 上限...
}

作用是給 speech 和 noise 均值設上界,避免模型被極端值推得太遠。

五、hangover 機制

1.策略作用

WebRTC VAD 的hangover 機制,用來在語音剛結束時 延長一小段時間繼續判為語音,避免抖動。

2.整體流程

檢測到語音 → num_of_speech++├─ 如果 num_of_speech <= kMaxSpeechFrames → over_hang = overhead1└─ 如果 num_of_speech >  kMaxSpeechFrames → over_hang = overhead2檢測到非語音├─ 如果 over_hang > 0 → 繼續輸出語音, over_hang--└─ 如果 over_hang = 0 → 輸出靜音

其中:

  • overhead1:給短語音的尾巴加一點點余量。
  • overhead2:給長語音的尾巴加更長的余量。
  • num_of_speech:用來區分當前語音段的“長/短”。
  • over_hang:用來平滑語音和靜音的切換,防止“斷斷續續”

3.對應代碼

1)非語音處理

if (!vadflag) {if (self->over_hang > 0) {vadflag = 2 + self->over_hang;self->over_hang--;}self->num_of_speech = 0;
}
  • 但 hangover 計數器 over_hang > 0:說明前面剛有語音 → 進入“延長語音”階段

    • 把 vadflag 設置為 2 + self->over_hang(這里 2 是特殊標記,表明這不是直接檢測出的語音,而是 hangover 延長出來的語音)
    • over_hang--:計數器遞減
  • 否則就徹底當作 silence

  • num_of_speech = 0:清零語音幀計數器

2)語音

else {self->num_of_speech++;if (self->num_of_speech > kMaxSpeechFrames) {self->num_of_speech = kMaxSpeechFrames;self->over_hang = overhead2;} else {self->over_hang = overhead1;}
}
  • num_of_speech++:累計連續語音幀數
  • 如果超過 kMaxSpeechFrames(最大語音幀數限制):
    • num_of_speech 固定到上限
    • over_hang = overhead2(長的 hangover 時間)
  • 否則:over_hang = overhead1(短的 hangover 時間)

總結

WebRTC VAD 展示了一個典型的“經典信號處理 + 工程優化”方案:

  • 在特征層面,采用子帶能量的對數刻畫,使得語音與噪聲分布更接近高斯;
  • 在模型層面,使用固定參數的雙高斯混合模型,結合局部和全局判決,提高魯棒性;
  • 在實現層面,大量利用 Q 格式、移位近似、預計算表,保證了低算力環境下的實時性;
  • 在動態性上,通過逐幀更新和邊界約束,使模型能逐漸適應環境變化。

當然,這種基于 GMM 的方法也有局限:在強噪聲、非平穩噪聲環境下可能誤判,且閾值調優高度依賴經驗。隨著算力提升,DNN/RNN 基的 VAD 在魯棒性上表現更優,但代價是更高的復雜度與延遲。

反正收藏也不會看,不如點個贊吧!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/97837.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/97837.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/97837.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【Redis】-- 主從復制

文章目錄1. 主從復制1.1 主從復制是怎么個事&#x1f914;1.2 拓撲結構1.2.1 一主一從拓撲1.2.2 一主多從拓撲1.2.3 樹形拓撲1.3 主從復制原理1.3.1 復制過程1.3.2 數據同步PSYNC1.3.2.1 replicationid/replid (復制id)1.3.2.2 復制偏移量維護1.3.3 psync運行流程1.3.4 全量復制…

開源炸場!阿里通義千問Qwen3-Next發布:80B參數僅激活3B,訓練成本降90%,長文本吞吐提升10倍?

開源炸場&#xff01;阿里通義千問Qwen3-Next發布&#xff1a;80B參數僅激活3B&#xff0c;訓練成本降90%&#xff0c;長文本吞吐提升10倍? 開源世界迎來震撼突破&#xff01; 通義千問團隊最新發布的Qwen3-Next架構&#xff0c;以其獨創的"小而精"設計理念&#x…

【C++入門】C++基礎

目錄 1. 命名空間 1.1 命名空間的創建和使用 2. 輸入輸出 2.1 輸出 2.2 輸入 3. 缺省參數 3.1 全缺省 3.2 半缺省 4.函數重載 4.1 為什么C支持重載而C語言不支持&#xff1f; 4.1.2 編譯的四個過程 4.2 extern是什么 5.引用 5.1 引用的特性 5.1.1 引用的“隱式類…

如何往mp4視頻添加封面圖和獲取封面圖?

前言&#xff1a;大家好&#xff0c;之前有給大家分享過mp4錄像的方案&#xff0c;今天給大家分享的內容是&#xff1a;如何在添加自定義的封面圖到mp4里面去&#xff0c;以及在進入回放mp4視頻列表的時候&#xff0c;怎么獲取mp4視頻里面的封面圖&#xff0c;當然這個獲取到的…

你的第一個Transformer模型:從零實現并訓練一個迷你ChatBot

點擊 “AladdinEdu&#xff0c;同學們用得起的【H卡】算力平臺”&#xff0c;注冊即送-H卡級別算力&#xff0c;80G大顯存&#xff0c;按量計費&#xff0c;靈活彈性&#xff0c;頂級配置&#xff0c;學生更享專屬優惠。 引言&#xff1a;破除神秘感&#xff0c;擁抱核心思想 …

【20期】滬深指數《實時交易數據》免費獲取股票數據API:PythonJava等5種語言調用實例演示與接口API文檔說明

? 隨著量化投資在金融市場的快速發展&#xff0c;高質量數據源已成為量化研究的核心基礎設施。本文將系統介紹股票量化分析中的數據獲取解決方案&#xff0c;涵蓋實時行情、歷史數據及基本面信息等關鍵數據類型。 本文將重點演示這些接口在以下技術棧中的實現&#xff1a; P…

RabbitMQ如何保障消息的可靠性

文章目錄什么是消息可靠性&#xff1f;RabbitMQ消息可靠性的三個維度1. 生產者到Exchange的可靠性2. Exchange到Queue的可靠性3. Queue到消費者的可靠性核心機制詳解Publisher Confirm機制消息持久化Mandatory參數消費者確認機制&#xff08;ACK&#xff09;最佳實踐建議1. 合理…

二十、DevOps落地:Jenkins基礎入門(一)

二十、DevOps落地&#xff1a;Jenkins基礎入門&#xff08;一&#xff09; 文章目錄二十、DevOps落地&#xff1a;Jenkins基礎入門&#xff08;一&#xff09;1、DevOps初識1.1 什么是DevOps1.2 DevOps相關工具鏈1.3 什么是CICD&#xff1f;1.4 持續集成CI介紹1.5 持續交付和持…

簡單易實現的數據校驗方法Checksum

簡單易實現的數據校驗方法Checksum 在數據傳輸中&#xff0c;Checksum&#xff08;校驗和&#xff09; 扮演著 “數據完整性哨兵” 的角色。它的主要作用是 快速檢測數據在傳輸過程中是否發生了錯誤 。 下面我將詳細解釋它的作用、工作原理、優缺點以及典型應用。 核心作用&…

再次深入學習深度學習|花書筆記1

我已經兩年沒有碰過深度學習了&#xff0c;寫此文記錄學習過程&#xff0c;加深理解。 深度學習再次深入學習深度學習|花書筆記1信息論第四節 數值計算中的問題上溢出 和 下溢出病態條件優化法再次深入學習深度學習|花書筆記1 這本書說的太繁瑣了&#xff0c;如果是想要基于這…

DeerFlow實踐:華為LTC流程的評審智能體設計

目錄 一、機制設計核心邏輯 二、4 個評審點智能體機制詳解 &#xff08;一&#xff09;立項決策&#xff08;ATI&#xff09;智能體機制 1. 知識調用與匹配 2. 評審校驗流程 3. 異常處理 &#xff08;二&#xff09;投標決策&#xff08;ATB&#xff09;智能體機制 1. …

C++與Lua交互:從原理到實踐指南

核心原理&#xff1a;Lua虛擬棧機制 C與Lua能夠高效交互的核心在于Lua虛擬棧的設計&#xff0c;這是一個精巧的中立通信區&#xff0c;解決了兩種語言間的本質差異&#xff1a;特性對比CLua語言類型靜態編譯型動態解釋型數據管理明確內存布局虛擬機統一管理類型系統編譯時確定運…

CSS 編碼規范

CSS 編碼規范1 CSS1.1 編碼規范1.1.1 【強制】所有聲明必須以分號結尾1.1.2 【推薦】使用 2 個空格縮進1.1.3 【推薦】選擇器與 { 之間保留一個空格1.1.4 【推薦】屬性值規范1.1.5 【推薦】組合器規范1.1.6 【推薦】逗號分隔規范1.1.7 【推薦】注釋規范1.1.8 【推薦】右大括號規…

ORA-12514:TNS:監聽程序當前無法識別連接描述符中請求的服務

已經不止一次自己本機電腦安裝的Oracle使用plsqldev軟件登入提示這個了.一般前一天還好好的&#xff0c;今天就不行了.好好總結一下吧&#xff0c;也共大家一起借鑒.主要原因還是數據的歸檔日志因為內部內存已經耗盡&#xff0c;不能在進行歸檔導致數據庫啟動異常&#xff0c;沒…

Spring框架的JDBC模板技術和事務管理

SpringJDBCJDBC模板技術概述JDBC的模板類的使用Spring框架的事務管理配置文件方式半注解的方式純注解的方式JDBC模板技術概述 什么是 JDBC 模板技術&#xff1f; JDBC 模板技術是 Spring 框架為簡化持久層&#xff08;數據庫操作&#xff09;編程而提供的一種封裝機制&#xf…

將文件部署到受管主機

目錄 1.ansible.builtin中用于創建、更新或刪除多行文本塊的模塊是什么 2.copy模塊的作用 3.fetch模塊的作用 4.file模塊的作用 5.lineinfile模塊的作用 6.stat模塊的作用 7.要確保受管主機上存在文件&#xff0c;類似touch命令功能&#xff0c;還能設置權限等的模塊及操作是怎…

Dell PowerEdge R620 服務器內存和硬盤罷工了

文章目錄前言調查原因查找解決方案硬盤問題內存問題總結前言 月黑風高夜&#xff0c;服務宕機時。做服務端技術的&#xff0c;誰還沒半夜遇到個服務掛掉的情況&#xff0c;而像我這種半兼職網管的工作&#xff0c;遇到機器問題的概率也就更大了&#xff0c;本來周五晚上寫完總…

2025:SourceTree 啟用/禁用Mercurial 或 Git,像素級細節

最近使用Git管理工具的時候&#xff0c;發現還是SourceTree好用些&#xff0c;但是使用SourceTree帶來一個問題&#xff1a;就是每次在重新打開SourceTree的時候&#xff0c;都會重新下載Mercurial.zip文件&#xff0c;查了一下&#xff0c;一般情況下我們是不需要使用Mercuria…

安卓 Google Maps 的使用和開發步驟

文章目錄1. main2. Android 谷歌地圖3. 源碼Reference1. main 在國內選擇的SDK可以是高德、百度、騰訊、xxxx等&#xff0c;但在國外&#xff0c;你首選是谷歌&#xff0c;因此要進行Google地圖的開發你首先要解決下面三個問題 VPN Google賬號 信用卡American Express&#x…

Linux -- 應用層協議Http

1.HTTP背景知識 HTTP協議&#xff1a;HTTP&#xff08;HyperText Transfer Protocol&#xff0c;超文本傳輸協議&#xff09;的本質是運行在 TCP/IP 協議族之上的 “應用層協議”&#xff0c;核心作用是定義客戶端&#xff08;如瀏覽器、APP&#xff09;與服務器之間的 “數據…