?無論是在音視頻錄制系統,還是音視頻通話系統、或視頻會議系統中,對從麥克風采集到的說話的聲音數據進行預處理,都是是非常必要的。
? ? ? 語音數據預處理主要包括:??降噪(Noise Reduction)、靜音檢測(Silence Detection/VAD)、自動增益(Automatic Gain Control, AGC)?? 。
一. 語音預處理的作用
? 我們先解釋一下,降噪、靜音檢測、自動增益,這些語音預處理分別起什么作用。
(1)降噪
? ? ? 降噪,用于消除背景噪聲,比如馬路車流聲、環境雜音等,以保留清晰的說話人聲。
? ? ? 更高級的,結合AI模型訓練,還可以消除電腦的風扇聲、鍵盤敲擊聲等等。
(2)靜音檢測
? ? ? 靜音檢測,又稱為語音活動檢測,用于識別音頻流中的靜音片段(沒有講話人聲),這樣可以簡化后續的編碼等環節,并可以節省傳遞所需要的帶寬。
(3)自動增益
? ? ? 自動增益,用于動態調整說話聲音的音量,使輸出電平保持穩定,以避免講話的聲音忽大忽小。
二. 實現語音預處理
? ? ? 接下來,我們使用C#實現一個Demo,這個Demo將從麥克風采集聲音數據,然后進行語音預處理,并且將處理后的聲音數據實時播放出來。Demo的運行效果如下圖所示:
? ? ??
? ? ? Demo 功能很簡單,那我們來具體看看代碼是如何實現的。
1. 創建采集器、預處理器、播放器
? ? ?麥克風聲音數據采樣率我們選擇16K、單聲道。?
WaveSampleRate sr = WaveSampleRate.S16k;
int channelCount = 1;//創建語音預處理器,開啟降噪、自動增益、靜音檢測
this.voicePreprocessor = CapturerFactory.CreateVoicePreprocessor(sr, channelCount, true ,true);
//創建麥克風采集器
this.microphoneCapturer = CapturerFactory.CreateMicrophoneCapturer(int.Parse(this.textBox_mic.Text), sr);
this.microphoneCapturer.AudioCaptured += new ESBasic.CbGeneric<byte[]>(microphoneCapturer_AudioCaptured);
//創建聲音播放器
this.audioPlayer = PlayerFactory.CreateAudioPlayer(int.Parse(this.textBox_speaker.Text), (int)sr, channelCount, 16, 2); this.microphoneCapturer.Start();
CreateVoicePreprocessor 方法的最后兩個參數可以指定在降噪的同時,是否開啟靜音檢測和自動增益功能。
2. 預處理語音數據
? ? ? 語音預處理器每次處理10ms的聲音數據,而現在的麥克風采集器每次采集的是20ms的PCM數據,所以,我們將其拆成兩個10ms數據,再提交給預處理器處理。
void microphoneCapturer_AudioCaptured(byte[] audioData)
{if (this.checkBox_enabled.Checked){//麥克風每次采集20ms數據,降噪器每次處理10ms數據。byte[] frame10ms1 = new byte[audioData.Length / 2];byte[] frame10ms2 = new byte[audioData.Length / 2];Buffer.BlockCopy(audioData, 0, frame10ms1, 0, frame10ms1.Length);Buffer.BlockCopy(audioData, frame10ms1.Length, frame10ms2, 0, frame10ms2.Length);this.HandleData(frame10ms1);this.HandleData(frame10ms2);return;}this.audioPlayer.Play(audioData);
}
(1)通過一個CheckBox勾選框來實時控制是否啟用語音預處理,這樣在測試時,就可以很方便的對比體驗開啟了語音預處理的效果。
(2)調用IVoicePreprocessor 的?Process 方法,就可以完成一幀語音數據(10ms)的預處理。如下所示:
private void HandleData(byte[] frame10ms)
{byte[] res = this.voicePreprocessor.Process(frame10ms); if (res == null) //靜音幀{++this.silenceFrameCountTotal;this.audioPlayer.Play(this.voicePreprocessor.SlienceFrame);}else{this.audioPlayer.Play(res);}
}
如果Process 方法返回的是null,表示檢測到該幀是靜音幀,于是,將內置的10ms靜音幀 SlienceFrame 提交給播放器去播放。
3. 統計靜音幀數量
一個語音幀是10ms,那么1秒鐘就有100個語音幀,程序中,我們統計了上一秒出現了多少個靜音幀,并在UI左下方顯示出來。
private volatile int silenceFrameCountTotal = 0;
private volatile int silenceFrameCountPre = 0;
private void timer1_Tick(object sender, EventArgs e)
{int delt = this.silenceFrameCountTotal - this.silenceFrameCountPre;this.silenceFrameCountPre = this.silenceFrameCountTotal;//顯示上一秒靜音幀數量。this.label_silenceFrameCount.Text = delt.ToString();
}
實際測試時可以發現,當不說話時,UI實時顯示1秒鐘出現的靜音幀是100個。
三. Demo源碼下載
? ? ? ? 源碼下載:VoicePreprocessDemo.rar