Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(374)

Side by Side Diff: webrtc/modules/audio_processing/intelligibility/intelligibility_enhancer.cc

Issue 1234463003: Integrate Intelligibility with APM (Closed) Base URL: https://chromium.googlesource.com/external/webrtc.git@master
Patch Set: Addressed comments Created 5 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 /* 1 /*
2 * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. 2 * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
3 * 3 *
4 * Use of this source code is governed by a BSD-style license 4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source 5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found 6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may 7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree. 8 * be found in the AUTHORS file in the root of the source tree.
9 */ 9 */
10 10
11 // 11 //
12 // Implements core class for intelligibility enhancer. 12 // Implements core class for intelligibility enhancer.
13 // 13 //
14 // Details of the model and algorithm can be found in the original paper: 14 // Details of the model and algorithm can be found in the original paper:
15 // http://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&arnumber=6882788 15 // http://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&arnumber=6882788
16 // 16 //
17 17
18 #include "webrtc/modules/audio_processing/intelligibility/intelligibility_enhanc er.h" 18 #include "webrtc/modules/audio_processing/intelligibility/intelligibility_enhanc er.h"
19 19
20 #include <math.h> 20 #include <math.h>
21 #include <stdlib.h> 21 #include <stdlib.h>
22
23 #include <algorithm> 22 #include <algorithm>
24 #include <numeric> 23 #include <numeric>
25 24
26 #include "webrtc/base/checks.h" 25 #include "webrtc/base/checks.h"
27 #include "webrtc/common_audio/vad/include/webrtc_vad.h"
28 #include "webrtc/common_audio/window_generator.h" 26 #include "webrtc/common_audio/window_generator.h"
27 #include "webrtc/common_audio/include/audio_util.h"
29 28
30 namespace webrtc { 29 namespace webrtc {
31 30
32 namespace { 31 namespace {
33 32
34 const int kErbResolution = 2; 33 const int kErbResolution = 2;
35 const int kWindowSizeMs = 2; 34 const int kWindowSizeMs = 2;
36 const int kChunkSizeMs = 10; // Size provided by APM. 35 const int kChunkSizeMs = 10; // Size provided by APM.
37 const float kClipFreq = 200.0f; 36 const float kClipFreq = 200.0f;
38 const float kConfigRho = 0.02f; // Default production and interpretation SNR. 37 const float kConfigRho = 0.02f; // Default production and interpretation SNR.
39 const float kKbdAlpha = 1.5f; 38 const float kKbdAlpha = 1.5f;
40 const float kLambdaBot = -1.0f; // Extreme values in bisection 39 const float kLambdaBot = -1.0f; // Extreme values in bisection
41 const float kLambdaTop = -10e-18f; // search for lamda. 40 const float kLambdaTop = -10e-18f; // search for lamda.
41 const float kMinNoise = 10e-18f;
42 42
43 } // namespace 43 } // namespace
44 44
45 using std::complex; 45 using std::complex;
46 using std::max; 46 using std::max;
47 using std::min; 47 using std::min;
48 using VarianceType = intelligibility::VarianceArray::StepType; 48 using VarianceType = intelligibility::VarianceArray::StepType;
49 49
50 IntelligibilityEnhancer::TransformCallback::TransformCallback( 50 IntelligibilityEnhancer::TransformCallback::TransformCallback(
51 IntelligibilityEnhancer* parent, 51 IntelligibilityEnhancer* parent,
52 IntelligibilityEnhancer::AudioSource source) 52 IntelligibilityEnhancer::AudioSource source)
53 : parent_(parent), source_(source) { 53 : parent_(parent), source_(source) {
54 } 54 }
55 55
56 void IntelligibilityEnhancer::TransformCallback::ProcessAudioBlock( 56 void IntelligibilityEnhancer::TransformCallback::ProcessAudioBlock(
57 const complex<float>* const* in_block, 57 const complex<float>* const* in_block,
58 int in_channels, 58 int in_channels,
59 int frames, 59 int frames,
60 int /* out_channels */, 60 int /* out_channels */,
61 complex<float>* const* out_block) { 61 complex<float>* const* out_block) {
62 DCHECK_EQ(parent_->freqs_, frames); 62 DCHECK_EQ(parent_->freqs_, frames);
63 for (int i = 0; i < in_channels; ++i) { 63 for (int i = 0; i < in_channels; ++i) {
64 parent_->DispatchAudio(source_, in_block[i], out_block[i]); 64 parent_->DispatchAudio(source_, in_block[i], out_block[i]);
65 } 65 }
66 } 66 }
67 67
68 IntelligibilityEnhancer::IntelligibilityEnhancer(int erb_resolution, 68 IntelligibilityEnhancer::IntelligibilityEnhancer()
69 int sample_rate_hz, 69 : IntelligibilityEnhancer(IntelligibilityEnhancer::Config()) {
70 int channels, 70 }
71 int cv_type, 71
72 float cv_alpha, 72 IntelligibilityEnhancer::IntelligibilityEnhancer(const Config& config)
73 int cv_win,
74 int analysis_rate,
75 int variance_rate,
76 float gain_limit)
77 : freqs_(RealFourier::ComplexLength( 73 : freqs_(RealFourier::ComplexLength(
78 RealFourier::FftOrder(sample_rate_hz * kWindowSizeMs / 1000))), 74 RealFourier::FftOrder(config.sample_rate_hz * kWindowSizeMs / 1000))),
79 window_size_(1 << RealFourier::FftOrder(freqs_)), 75 window_size_(1 << RealFourier::FftOrder(freqs_)),
80 chunk_length_(sample_rate_hz * kChunkSizeMs / 1000), 76 chunk_length_(config.sample_rate_hz * kChunkSizeMs / 1000),
81 bank_size_(GetBankSize(sample_rate_hz, erb_resolution)), 77 bank_size_(GetBankSize(config.sample_rate_hz, kErbResolution)),
82 sample_rate_hz_(sample_rate_hz), 78 sample_rate_hz_(config.sample_rate_hz),
83 erb_resolution_(erb_resolution), 79 erb_resolution_(kErbResolution),
84 channels_(channels), 80 num_capture_channels_(config.num_capture_channels),
85 analysis_rate_(analysis_rate), 81 num_render_channels_(config.num_render_channels),
86 variance_rate_(variance_rate), 82 analysis_rate_(config.analysis_rate),
83 capture_vad_thresh_(config.capture_vad_thresh),
84 render_vad_thresh_(config.render_vad_thresh),
85 activate_snr_thresh_(config.activate_snr_thresh),
86 deactivate_snr_thresh_(config.deactivate_snr_thresh),
87 active_(false),
88 deactivating_(false),
87 clear_variance_(freqs_, 89 clear_variance_(freqs_,
88 static_cast<VarianceType>(cv_type), 90 config.var_type,
89 cv_win, 91 config.var_window_size,
90 cv_alpha), 92 config.var_decay_rate),
91 noise_variance_(freqs_, VarianceType::kStepInfinite, 475, 0.01f), 93 noise_variance_(freqs_,
94 config.var_type,
95 config.var_window_size,
96 config.var_decay_rate),
92 filtered_clear_var_(new float[bank_size_]), 97 filtered_clear_var_(new float[bank_size_]),
93 filtered_noise_var_(new float[bank_size_]), 98 filtered_noise_var_(new float[bank_size_]),
94 filter_bank_(bank_size_), 99 filter_bank_(bank_size_),
95 center_freqs_(new float[bank_size_]), 100 center_freqs_(new float[bank_size_]),
96 rho_(new float[bank_size_]), 101 rho_(new float[bank_size_]),
97 gains_eq_(new float[bank_size_]), 102 gains_eq_(new float[bank_size_]),
98 gain_applier_(freqs_, gain_limit), 103 gain_applier_(freqs_, config.gain_change_limit),
99 temp_out_buffer_(nullptr), 104 temp_render_out_buffer_(chunk_length_, num_render_channels_),
100 input_audio_(new float* [channels]), 105 temp_capture_out_buffer_(chunk_length_, num_capture_channels_),
101 kbd_window_(new float[window_size_]), 106 kbd_window_(new float[window_size_]),
102 render_callback_(this, AudioSource::kRenderStream), 107 render_callback_(this, AudioSource::kRenderStream),
103 capture_callback_(this, AudioSource::kCaptureStream), 108 capture_callback_(this, AudioSource::kCaptureStream),
104 block_count_(0), 109 block_count_(0),
105 analysis_step_(0), 110 analysis_step_(0),
106 vad_high_(WebRtcVad_Create()), 111 using_capture_vad_(true),
107 vad_low_(WebRtcVad_Create()), 112 using_render_vad_(true),
108 vad_tmp_buffer_(new int16_t[chunk_length_]) { 113 vad_tmp_buffer_(new int16_t[chunk_length_]) {
109 DCHECK_LE(kConfigRho, 1.0f); 114 DCHECK_LE(config.rho, 1.0f);
110 115
111 CreateErbBank(); 116 CreateErbBank();
112 117
113 WebRtcVad_Init(vad_high_);
114 WebRtcVad_set_mode(vad_high_, 0); // High likelihood of speech.
115 WebRtcVad_Init(vad_low_);
116 WebRtcVad_set_mode(vad_low_, 3); // Low likelihood of speech.
117
118 temp_out_buffer_ = static_cast<float**>(
119 malloc(sizeof(*temp_out_buffer_) * channels_ +
120 sizeof(**temp_out_buffer_) * chunk_length_ * channels_));
121 for (int i = 0; i < channels_; ++i) {
122 temp_out_buffer_[i] =
123 reinterpret_cast<float*>(temp_out_buffer_ + channels_) +
124 chunk_length_ * i;
125 }
126
127 // Assumes all rho equal. 118 // Assumes all rho equal.
128 for (int i = 0; i < bank_size_; ++i) { 119 for (int i = 0; i < bank_size_; ++i) {
129 rho_[i] = kConfigRho * kConfigRho; 120 rho_[i] = config.rho * config.rho;
130 } 121 }
131 122
132 float freqs_khz = kClipFreq / 1000.0f; 123 float freqs_khz = kClipFreq / 1000.0f;
133 int erb_index = static_cast<int>(ceilf( 124 int erb_index = static_cast<int>(ceilf(
134 11.17f * logf((freqs_khz + 0.312f) / (freqs_khz + 14.6575f)) + 43.0f)); 125 11.17f * logf((freqs_khz + 0.312f) / (freqs_khz + 14.6575f)) + 43.0f));
135 start_freq_ = max(1, erb_index * kErbResolution); 126 start_freq_ = max(1, erb_index * erb_resolution_);
136 127
137 WindowGenerator::KaiserBesselDerived(kKbdAlpha, window_size_, 128 WindowGenerator::KaiserBesselDerived(kKbdAlpha, window_size_,
138 kbd_window_.get()); 129 kbd_window_.get());
139 render_mangler_.reset(new LappedTransform( 130 render_mangler_.reset(new LappedTransform(
140 channels_, channels_, chunk_length_, kbd_window_.get(), window_size_, 131 num_render_channels_, num_render_channels_, chunk_length_,
141 window_size_ / 2, &render_callback_)); 132 kbd_window_.get(), window_size_, window_size_ / 2, &render_callback_));
142 capture_mangler_.reset(new LappedTransform( 133 capture_mangler_.reset(new LappedTransform(
143 channels_, channels_, chunk_length_, kbd_window_.get(), window_size_, 134 num_capture_channels_, num_capture_channels_, chunk_length_,
144 window_size_ / 2, &capture_callback_)); 135 kbd_window_.get(), window_size_, window_size_ / 2, &capture_callback_));
145 } 136 }
146 137
147 IntelligibilityEnhancer::~IntelligibilityEnhancer() { 138 void IntelligibilityEnhancer::ProcessRenderAudio(float* const* audio,
148 WebRtcVad_Free(vad_low_); 139 int sample_rate_hz,
149 WebRtcVad_Free(vad_high_); 140 int num_channels,
150 free(temp_out_buffer_); 141 float voice_probability) {
142 render_voice_probability_ = voice_probability;
143 using_render_vad_ = false;
144 ProcessRenderAudio(audio, sample_rate_hz, num_channels);
151 } 145 }
152 146
153 void IntelligibilityEnhancer::ProcessRenderAudio(float* const* audio) { 147 void IntelligibilityEnhancer::ProcessRenderAudio(float* const* audio,
154 for (int i = 0; i < chunk_length_; ++i) { 148 int sample_rate_hz,
155 vad_tmp_buffer_[i] = (int16_t)audio[0][i]; 149 int num_channels) {
150 CHECK_EQ(sample_rate_hz_, sample_rate_hz);
151 CHECK_EQ(num_render_channels_, num_channels);
152
153 if (using_render_vad_) {
154 FloatToS16(audio[0], chunk_length_, vad_tmp_buffer_.get());
155 render_vad_.ProcessChunk(vad_tmp_buffer_.get(), chunk_length_,
156 sample_rate_hz_);
157 render_voice_probability_ = render_vad_.last_voice_probability();
156 } 158 }
157 has_voice_low_ = WebRtcVad_Process(vad_low_, sample_rate_hz_,
158 vad_tmp_buffer_.get(), chunk_length_) == 1;
159 159
160 // Process and enhance chunk of |audio| 160 if (render_voice_probability_ >= render_vad_thresh_ || active_) {
161 render_mangler_->ProcessChunk(audio, temp_out_buffer_); 161 render_mangler_->ProcessChunk(audio, temp_render_out_buffer_.channels());
162 }
162 163
163 for (int i = 0; i < channels_; ++i) { 164 for (int i = 0; i < num_render_channels_; ++i) {
164 memcpy(audio[i], temp_out_buffer_[i], 165 memcpy(audio[i], temp_render_out_buffer_.channels()[i],
165 chunk_length_ * sizeof(**temp_out_buffer_)); 166 chunk_length_ * sizeof(float));
aluebs-webrtc 2015/07/23 00:35:26 sizeof(**audio)
ekm 2015/07/23 00:52:15 Done.
166 } 167 }
167 } 168 }
168 169
169 void IntelligibilityEnhancer::ProcessCaptureAudio(float* const* audio) { 170 void IntelligibilityEnhancer::AnalyzeCaptureAudio(float* const* audio,
170 for (int i = 0; i < chunk_length_; ++i) { 171 int sample_rate_hz,
171 vad_tmp_buffer_[i] = (int16_t)audio[0][i]; 172 int num_channels,
173 float voice_probability) {
174 capture_voice_probability_ = voice_probability;
175 using_capture_vad_ = false;
176 AnalyzeCaptureAudio(audio, sample_rate_hz, num_channels);
177 }
178
179 void IntelligibilityEnhancer::AnalyzeCaptureAudio(float* const* audio,
180 int sample_rate_hz,
181 int num_channels) {
182 CHECK_EQ(sample_rate_hz_, sample_rate_hz);
183 CHECK_EQ(num_capture_channels_, num_channels);
184
185 if (using_capture_vad_) {
186 FloatToS16(audio[0], chunk_length_, vad_tmp_buffer_.get());
187 capture_vad_.ProcessChunk(vad_tmp_buffer_.get(), chunk_length_,
188 sample_rate_hz_);
189 capture_voice_probability_ = capture_vad_.last_voice_probability();
172 } 190 }
173 // TODO(bercic): The VAD was always detecting voice in the noise stream,
174 // no matter what the aggressiveness, so it was temporarily disabled here.
175 191
176 #if 0 192 if (capture_voice_probability_ <= capture_vad_thresh_) {
177 if (WebRtcVad_Process(vad_high_, sample_rate_hz_, vad_tmp_buffer_.get(), 193 capture_mangler_->ProcessChunk(audio, temp_capture_out_buffer_.channels());
178 chunk_length_) == 1) { 194 }
179 printf("capture HAS speech\n");
180 return;
181 }
182 printf("capture NO speech\n");
183 #endif
184
185 capture_mangler_->ProcessChunk(audio, temp_out_buffer_);
186 } 195 }
187 196
188 void IntelligibilityEnhancer::DispatchAudio( 197 void IntelligibilityEnhancer::DispatchAudio(
189 IntelligibilityEnhancer::AudioSource source, 198 IntelligibilityEnhancer::AudioSource source,
190 const complex<float>* in_block, 199 const complex<float>* in_block,
191 complex<float>* out_block) { 200 complex<float>* out_block) {
192 switch (source) { 201 switch (source) {
193 case kRenderStream: 202 case kRenderStream:
194 ProcessClearBlock(in_block, out_block); 203 ProcessClearBlock(in_block, out_block);
195 break; 204 break;
196 case kCaptureStream: 205 case kCaptureStream:
197 ProcessNoiseBlock(in_block, out_block); 206 ProcessNoiseBlock(in_block, out_block);
198 break; 207 break;
199 } 208 }
200 } 209 }
201 210
202 void IntelligibilityEnhancer::ProcessClearBlock(const complex<float>* in_block, 211 void IntelligibilityEnhancer::ProcessClearBlock(const complex<float>* in_block,
203 complex<float>* out_block) { 212 complex<float>* out_block) {
204 if (block_count_ < 2) { 213 if (block_count_ < 2) {
205 memset(out_block, 0, freqs_ * sizeof(*out_block)); 214 memset(out_block, 0, freqs_ * sizeof(*out_block));
206 ++block_count_; 215 ++block_count_;
207 return; 216 return;
208 } 217 }
209 218
210 // For now, always assumes enhancement is necessary. 219 if (render_voice_probability_ >= render_vad_thresh_) {
211 // TODO(ekmeyerson): Change to only enhance if necessary,
212 // based on experiments with different cutoffs.
213 if (has_voice_low_ || true) {
214 clear_variance_.Step(in_block, false); 220 clear_variance_.Step(in_block, false);
215 const float power_target = std::accumulate( 221 if (active_ && !deactivating_ &&
216 clear_variance_.variance(), clear_variance_.variance() + freqs_, 0.0f); 222 block_count_ % analysis_rate_ == analysis_rate_ - 1) {
217 223 const float power_target = std::accumulate(
218 if (block_count_ % analysis_rate_ == analysis_rate_ - 1) { 224 clear_variance_.variance(), clear_variance_.variance() + freqs_, 0.f);
219 AnalyzeClearBlock(power_target); 225 AnalyzeClearBlock(power_target);
220 ++analysis_step_; 226 ++analysis_step_;
221 if (analysis_step_ == variance_rate_) {
222 analysis_step_ = 0;
223 clear_variance_.Clear();
224 noise_variance_.Clear();
225 }
226 } 227 }
227 ++block_count_; 228 ++block_count_;
228 } 229 }
229 230
230 /* efidata(n,:) = sqrt(b(n)) * fidata(n,:) */ 231 UpdateActivity();
231 gain_applier_.Apply(in_block, out_block); 232 if (active_) {
233 // efidata(n,:) = sqrt(b(n)) * fidata(n,:)
234 gain_applier_.Apply(in_block, out_block);
235 }
232 } 236 }
233 237
234 void IntelligibilityEnhancer::AnalyzeClearBlock(float power_target) { 238 void IntelligibilityEnhancer::AnalyzeClearBlock(float power_target) {
235 FilterVariance(clear_variance_.variance(), filtered_clear_var_.get()); 239 FilterVariance(clear_variance_.variance(), filtered_clear_var_.get());
236 FilterVariance(noise_variance_.variance(), filtered_noise_var_.get()); 240 FilterVariance(noise_variance_.variance(), filtered_noise_var_.get());
237 241
238 SolveForGainsGivenLambda(kLambdaTop, start_freq_, gains_eq_.get()); 242 SolveForGainsGivenLambda(kLambdaTop, start_freq_, gains_eq_.get());
239 const float power_top = 243 const float power_top =
240 DotProduct(gains_eq_.get(), filtered_clear_var_.get(), bank_size_); 244 DotProduct(gains_eq_.get(), filtered_clear_var_.get(), bank_size_);
241 SolveForGainsGivenLambda(kLambdaBot, start_freq_, gains_eq_.get()); 245 SolveForGainsGivenLambda(kLambdaBot, start_freq_, gains_eq_.get());
(...skipping 146 matching lines...) Expand 10 before | Expand all | Expand 10 after
388 sols[n] = fmax(0, sols[n]); 392 sols[n] = fmax(0, sols[n]);
389 } 393 }
390 } 394 }
391 395
392 void IntelligibilityEnhancer::FilterVariance(const float* var, float* result) { 396 void IntelligibilityEnhancer::FilterVariance(const float* var, float* result) {
393 for (int i = 0; i < bank_size_; ++i) { 397 for (int i = 0; i < bank_size_; ++i) {
394 result[i] = DotProduct(filter_bank_[i].data(), var, freqs_); 398 result[i] = DotProduct(filter_bank_[i].data(), var, freqs_);
395 } 399 }
396 } 400 }
397 401
402 float IntelligibilityEnhancer::SNR() {
403 float total_clear_var = std::accumulate(
404 clear_variance_.variance(), clear_variance_.variance() + freqs_, 0.f);
405 float total_noise_var =
406 std::accumulate(noise_variance_.variance(),
407 noise_variance_.variance() + freqs_, kMinNoise);
408 return total_clear_var / total_noise_var;
409 }
410
411 void IntelligibilityEnhancer::UpdateActivity() {
412 const float snr = SNR();
413 if (snr <= activate_snr_thresh_) {
414 active_ = true;
415 deactivating_ = false;
416 } else if (active_ && !deactivating_ && snr >= deactivate_snr_thresh_) {
417 gain_applier_.Clear();
418 deactivating_ = true;
419 } else if (deactivating_ && gain_applier_.IsIdentity()) {
420 active_ = false;
421 deactivating_ = false;
422 }
423 }
424
398 float IntelligibilityEnhancer::DotProduct(const float* a, 425 float IntelligibilityEnhancer::DotProduct(const float* a,
399 const float* b, 426 const float* b,
400 int length) { 427 int length) {
401 float ret = 0.0f; 428 float ret = 0.0f;
402 429
403 for (int i = 0; i < length; ++i) { 430 for (int i = 0; i < length; ++i) {
404 ret = fmaf(a[i], b[i], ret); 431 ret = fmaf(a[i], b[i], ret);
405 } 432 }
406 return ret; 433 return ret;
407 } 434 }
408 435
409 } // namespace webrtc 436 } // namespace webrtc
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698