本文主要介紹WebRTC的APM。
現在主要介紹一下audio_processing.h。
首先插入了幾個類,這些都是audio_processing的核心模塊。
class
AudioFrame;
class
EchoCancellation;
class
EchoControlMobile;
class
GainControl;
class
HighPassFilter;
class
LevelEstimator;
class
NoiseSuppression;
class
VoiceDetection;
AudioFrame:主要記錄了通道基本信息,數據,VAD標志時間戳,采樣頻率,信道數等。
EchoCancellation:回聲消除模塊(AEC),在使用外置揚聲器的時候應該使用,有些使用耳麥通訊的情況也會存在回聲(因為麥克風與揚聲器有空間或者電的弱耦合),如果影響了通話也應該開啟。
EchoControlMobile:回聲抑制模塊(AES),這個模塊和回聲消除模塊功能相似,但是實現方法不一樣。運算量遠遠小於回聲消除模塊。非常適合移動平台使用。但是對語音損傷大。
GainControl:增益控制模塊(AGC),這個模塊使用了語音的特征對系統硬件音量和輸出的信號大小進行調節。硬件上可以控制輸入音量。軟件上只能調節原來信號的幅度,如果對原來就已經破音的信號,或者本來輸入就比較小的信號就無能為力了。
HighPassFilter:高通濾波器,抑制不需要的低頻信號。可以根據需要修改參數選擇相應的截止頻率。對於某些有工頻干擾的設備需要使用高通濾波器。
LevelEstimator:估計信號的能量值。
NoiseSuppression:噪聲抑制模塊(NS/SE),該模塊一般應用在有環境噪聲的情況,或者是麥克風采集到的數據有明顯噪聲的情況。
VoiceDetection:語音激活檢測模塊(VAD),該模塊用於檢測語音是否出現。用於編解碼以及后續相關處理。
APM分為兩個流,一個近端流,一個遠端流。近端(Near-end)流是指從麥克風進入的數據;遠端(Far-end)流是指接收到的數據。現在分別介紹一下,這部分代碼在audio_processing_impl.cc里。
far_end流代碼:
int AudioProcessingImpl::AnalyzeReverseStreamLocked() {
AudioBuffer* ra = render_audio_.get(); // For brevity.
if (rev_proc_format_.rate() == kSampleRate32kHz) {
for (int i = 0; i < rev_proc_format_.num_channels(); i++) {
// Split into low and high band.
WebRtcSpl_AnalysisQMF(ra->data(i),
ra->samples_per_channel(),
ra->low_pass_split_data(i),
ra->high_pass_split_data(i),
ra->filter_states(i)->analysis_filter_state1,
ra->filter_states(i)->analysis_filter_state2);
}
}
RETURN_ON_ERR(echo_cancellation_->ProcessRenderAudio(ra));
RETURN_ON_ERR(echo_control_mobile_->ProcessRenderAudio(ra));
RETURN_ON_ERR(gain_control_->ProcessRenderAudio(ra));
return kNoError;
}
上述代碼可以看出far-end獲得數據后主要有4個步驟的處理。
1、判斷是否是32k信號,采取相應的分頻策略;
2、AEC流程,記錄AEC中的far-end及其相關運算;
3、AES流程,記錄AES中的far-end及其相關運算;
4、AGC流程,計算far-end及其相關特征。
near-end流代碼:
int AudioProcessingImpl::ProcessStreamLocked() {
#ifdef WEBRTC_AUDIOPROC_DEBUG_DUMP
if (debug_file_->Open()) {
audioproc::Stream* msg = event_msg_->mutable_stream();
msg->set_delay(stream_delay_ms_);
msg->set_drift(echo_cancellation_->stream_drift_samples());
msg->set_level(gain_control_->stream_analog_level());
msg->set_keypress(key_pressed_);
}
#endif
AudioBuffer* ca = capture_audio_.get(); // For brevity.
bool data_processed = is_data_processed();
if (analysis_needed(data_processed)) {
for (int i = 0; i < fwd_proc_format_.num_channels(); i++) {
// Split into a low and high band.
WebRtcSpl_AnalysisQMF(ca->data(i),
ca->samples_per_channel(),
ca->low_pass_split_data(i),
ca->high_pass_split_data(i),
ca->filter_states(i)->analysis_filter_state1,
ca->filter_states(i)->analysis_filter_state2);
}
}
RETURN_ON_ERR(high_pass_filter_->ProcessCaptureAudio(ca));
RETURN_ON_ERR(gain_control_->AnalyzeCaptureAudio(ca));
RETURN_ON_ERR(echo_cancellation_->ProcessCaptureAudio(ca));
if (echo_control_mobile_->is_enabled() && noise_suppression_->is_enabled()) {
ca->CopyLowPassToReference();
}
RETURN_ON_ERR(noise_suppression_->ProcessCaptureAudio(ca));
RETURN_ON_ERR(echo_control_
mobile_->ProcessCaptureAudio(ca));
RETURN_ON_ERR(voice_detection_->ProcessCaptureAudio(ca));
RETURN_ON_ERR(gain_control_->ProcessCaptureAudio(ca));
if (synthesis_needed(data_processed)) {
for (int i = 0; i < fwd_proc_format_.num_channels(); i++) {
// Recombine low and high bands.
WebRtcSpl_SynthesisQMF(ca->low_pass_split_data(i),
ca->high_pass_split_data(i),
ca->samples_per_split_channel(),
ca->data(i),
ca->filter_states(i)->synthesis_filter_state1,
ca->filter_states(i)->synthesis_filter_state2);
}
}
// The level estimator operates on the recombined data.
RETURN_ON_ERR(level_estimator_->ProcessStream(ca));
was_stream_delay_set_ = false;
return kNoError;
}
其中包括七個步驟:1、分頻;2、高通濾波;3、硬件音量控制;4、AEC;5、NS;6、AES;7、VAD;8、AGC;9、綜合。
可見near-end的處理全面,流程清晰。可以根據實際需要打開不同的模塊,適應不同場景的需要,對於一般通訊系統來說具有正面的改善效果。但是在實際工作中也發現了一些流程上隱患。另外就是該結構的各個模塊處理相對獨立耦合低,本來應該是一個優良的特性,然而在復雜情況的信號處理難以到達目標效果。由於低耦合造成的運算量浪費更加是無法避免的。