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

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

Issue 1207353002: Add new variance update option and unittests for intelligibility (Closed) Base URL: https://chromium.googlesource.com/external/webrtc.git@master
Patch Set: Renamed tests + minor changes 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 helper functions and classes for intelligibility enhancement. 12 // Implements helper functions and classes for intelligibility enhancement.
13 // 13 //
14 14
15 #include "webrtc/modules/audio_processing/intelligibility/intelligibility_utils. h" 15 #include "webrtc/modules/audio_processing/intelligibility/intelligibility_utils. h"
16 16
17 #include <algorithm> 17 #include <algorithm>
18 #include <cmath> 18 #include <cmath>
hlundin-webrtc 2015/06/30 14:00:53 math.h and string.h
ekm 2015/07/01 23:48:26 Done.
19 #include <cstring> 19 #include <cstring>
20 20
21 using std::complex; 21 using std::complex;
22 using std::min;
22 23
23 namespace { 24 namespace webrtc {
25
26 namespace intelligibility {
24 27
25 // Return |current| changed towards |target|, with the change being at most 28 // Return |current| changed towards |target|, with the change being at most
hlundin-webrtc 2015/06/30 14:00:53 Avoid copying the declaration comment to the imple
ekm 2015/07/01 23:48:26 Done.
26 // |limit|. 29 // |limit|.
27 inline float UpdateFactor(float target, float current, float limit) { 30 float UpdateFactor(float target, float current, float limit) {
28 float delta = fabsf(target - current); 31 float delta = fabsf(target - current);
29 float sign = copysign(1.0f, target - current); 32 float sign = copysign(1.0f, target - current);
30 return current + sign * fminf(delta, limit); 33 return current + sign * fminf(delta, limit);
31 } 34 }
32 35
33 // std::isfinite for complex numbers. 36 // std::isfinite for complex numbers.
34 inline bool cplxfinite(complex<float> c) { 37 bool cplxfinite(complex<float> c) {
35 return std::isfinite(c.real()) && std::isfinite(c.imag()); 38 return std::isfinite(c.real()) && std::isfinite(c.imag());
36 } 39 }
37 40
38 // std::isnormal for complex numbers. 41 // std::isnormal for complex numbers.
39 inline bool cplxnormal(complex<float> c) { 42 bool cplxnormal(complex<float> c) {
40 return std::isnormal(c.real()) && std::isnormal(c.imag()); 43 return std::isnormal(c.real()) && std::isnormal(c.imag());
41 } 44 }
42 45
43 // Apply a small fudge to degenerate complex values. The numbers in the array 46 // Apply a small fudge to degenerate complex values. The numbers in the array
44 // were chosen randomly, so that even a series of all zeroes has some small 47 // were chosen randomly, so that even a series of all zeroes has some small
45 // variability. 48 // variability.
46 inline complex<float> zerofudge(complex<float> c) { 49 complex<float> zerofudge(complex<float> c) {
47 const static complex<float> fudge[7] = {{0.001f, 0.002f}, 50 const static complex<float> fudge[7] = {{0.001f, 0.002f},
48 {0.008f, 0.001f}, 51 {0.008f, 0.001f},
49 {0.003f, 0.008f}, 52 {0.003f, 0.008f},
50 {0.0006f, 0.0009f}, 53 {0.0006f, 0.0009f},
51 {0.001f, 0.004f}, 54 {0.001f, 0.004f},
52 {0.003f, 0.004f}, 55 {0.003f, 0.004f},
53 {0.002f, 0.009f}}; 56 {0.002f, 0.009f}};
54 static int fudge_index = 0; 57 static int fudge_index = 0;
55 if (cplxfinite(c) && !cplxnormal(c)) { 58 if (cplxfinite(c) && !cplxnormal(c)) {
56 fudge_index = (fudge_index + 1) % 7; 59 fudge_index = (fudge_index + 1) % 7;
57 return c + fudge[fudge_index]; 60 return c + fudge[fudge_index];
58 } 61 }
59 return c; 62 return c;
60 } 63 }
61 64
62 // Incremental mean computation. Return the mean of the series with the 65 // Incremental mean computation. Return the mean of the series with the
63 // mean |mean| with added |data|. 66 // mean |mean| with added |data|.
64 inline complex<float> NewMean(complex<float> mean, 67 complex<float> NewMean(complex<float> mean,
65 complex<float> data, 68 complex<float> data,
66 int count) { 69 int count) {
67 return mean + (data - mean) / static_cast<float>(count); 70 return mean + (data - mean) / static_cast<float>(count);
68 } 71 }
69 72
70 inline void AddToMean(complex<float> data, int count, complex<float>* mean) { 73 void AddToMean(complex<float> data, int count, complex<float>* mean) {
71 (*mean) = NewMean(*mean, data, count); 74 (*mean) = NewMean(*mean, data, count);
72 } 75 }
73 76
74 } // namespace
75
76 using std::min;
77
78 namespace webrtc {
79
80 namespace intelligibility {
81 77
82 static const int kWindowBlockSize = 10; 78 static const int kWindowBlockSize = 10;
83 79
84 VarianceArray::VarianceArray(int freqs, 80 VarianceArray::VarianceArray(int freqs,
85 StepType type, 81 StepType type,
86 int window_size, 82 int window_size,
87 float decay) 83 float decay)
88 : running_mean_(new complex<float>[freqs]()), 84 : running_mean_(new complex<float>[freqs]()),
89 running_mean_sq_(new complex<float>[freqs]()), 85 running_mean_sq_(new complex<float>[freqs]()),
90 sub_running_mean_(new complex<float>[freqs]()), 86 sub_running_mean_(new complex<float>[freqs]()),
91 sub_running_mean_sq_(new complex<float>[freqs]()), 87 sub_running_mean_sq_(new complex<float>[freqs]()),
92 variance_(new float[freqs]()), 88 variance_(new float[freqs]()),
93 conj_sum_(new float[freqs]()), 89 conj_sum_(new float[freqs]()),
94 freqs_(freqs), 90 freqs_(freqs),
95 window_size_(window_size), 91 window_size_(window_size),
96 decay_(decay), 92 decay_(decay),
97 history_cursor_(0), 93 history_cursor_(0),
98 count_(0), 94 count_(0),
99 array_mean_(0.0f) { 95 array_mean_(0.0f),
96 buffer_full_(false) {
100 history_.reset(new rtc::scoped_ptr<complex<float>[]>[freqs_]()); 97 history_.reset(new rtc::scoped_ptr<complex<float>[]>[freqs_]());
101 for (int i = 0; i < freqs_; ++i) { 98 for (int i = 0; i < freqs_; ++i) {
102 history_[i].reset(new complex<float>[window_size_]()); 99 history_[i].reset(new complex<float>[window_size_]());
103 } 100 }
104 subhistory_.reset(new rtc::scoped_ptr<complex<float>[]>[freqs_]()); 101 subhistory_.reset(new rtc::scoped_ptr<complex<float>[]>[freqs_]());
105 for (int i = 0; i < freqs_; ++i) { 102 for (int i = 0; i < freqs_; ++i) {
106 subhistory_[i].reset(new complex<float>[window_size_]()); 103 subhistory_[i].reset(new complex<float>[window_size_]());
107 } 104 }
108 subhistory_sq_.reset(new rtc::scoped_ptr<complex<float>[]>[freqs_]()); 105 subhistory_sq_.reset(new rtc::scoped_ptr<complex<float>[]>[freqs_]());
109 for (int i = 0; i < freqs_; ++i) { 106 for (int i = 0; i < freqs_; ++i) {
110 subhistory_sq_[i].reset(new complex<float>[window_size_]()); 107 subhistory_sq_[i].reset(new complex<float>[window_size_]());
111 } 108 }
112 switch (type) { 109 switch (type) {
113 case kStepInfinite: 110 case kStepInfinite:
114 step_func_ = &VarianceArray::InfiniteStep; 111 step_func_ = &VarianceArray::InfiniteStep;
115 break; 112 break;
116 case kStepDecaying: 113 case kStepDecaying:
117 step_func_ = &VarianceArray::DecayStep; 114 step_func_ = &VarianceArray::DecayStep;
118 break; 115 break;
119 case kStepWindowed: 116 case kStepWindowed:
120 step_func_ = &VarianceArray::WindowedStep; 117 step_func_ = &VarianceArray::WindowedStep;
121 break; 118 break;
122 case kStepBlocked: 119 case kStepBlocked:
123 step_func_ = &VarianceArray::BlockedStep; 120 step_func_ = &VarianceArray::BlockedStep;
124 break; 121 break;
122 case kStepBlockBasedMovingAverage:
123 step_func_ = &VarianceArray::BlockBasedMovingAverage;
124 break;
125 } 125 }
126 } 126 }
127 127
128 // Compute the variance with Welford's algorithm, adding some fudge to 128 // Compute the variance with Welford's algorithm, adding some fudge to
129 // the input in case of all-zeroes. 129 // the input in case of all-zeroes.
130 void VarianceArray::InfiniteStep(const complex<float>* data, bool skip_fudge) { 130 void VarianceArray::InfiniteStep(const complex<float>* data, bool skip_fudge) {
131 array_mean_ = 0.0f; 131 array_mean_ = 0.0f;
132 ++count_; 132 ++count_;
133 for (int i = 0; i < freqs_; ++i) { 133 for (int i = 0; i < freqs_; ++i) {
134 complex<float> sample = data[i]; 134 complex<float> sample = data[i];
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after
216 history_cursor_ = (history_cursor_ + 1) % window_size_; 216 history_cursor_ = (history_cursor_ + 1) % window_size_;
217 ++count_; 217 ++count_;
218 } 218 }
219 219
220 // Variance with a window of blocks. Within each block, the variances are 220 // Variance with a window of blocks. Within each block, the variances are
221 // recomputed from scratch at every stp, using |Var(X) = E(X^2) - E^2(X)|. 221 // recomputed from scratch at every stp, using |Var(X) = E(X^2) - E^2(X)|.
222 // Once a block is filled with kWindowBlockSize samples, it is added to the 222 // Once a block is filled with kWindowBlockSize samples, it is added to the
223 // history window and a new block is started. The variances for the window 223 // history window and a new block is started. The variances for the window
224 // are recomputed from scratch at each of these transitions. 224 // are recomputed from scratch at each of these transitions.
225 void VarianceArray::BlockedStep(const complex<float>* data, bool /*dummy*/) { 225 void VarianceArray::BlockedStep(const complex<float>* data, bool /*dummy*/) {
226 int blocks = min(window_size_, history_cursor_); 226 int blocks = min(window_size_, history_cursor_ + 1);
227 for (int i = 0; i < freqs_; ++i) { 227 for (int i = 0; i < freqs_; ++i) {
228 AddToMean(data[i], count_ + 1, &sub_running_mean_[i]); 228 AddToMean(data[i], count_ + 1, &sub_running_mean_[i]);
229 AddToMean(data[i] * std::conj(data[i]), count_ + 1, 229 AddToMean(data[i] * std::conj(data[i]), count_ + 1,
230 &sub_running_mean_sq_[i]); 230 &sub_running_mean_sq_[i]);
231 subhistory_[i][history_cursor_ % window_size_] = sub_running_mean_[i]; 231 subhistory_[i][history_cursor_ % window_size_] = sub_running_mean_[i];
232 subhistory_sq_[i][history_cursor_ % window_size_] = sub_running_mean_sq_[i]; 232 subhistory_sq_[i][history_cursor_ % window_size_] = sub_running_mean_sq_[i];
233 233
234 variance_[i] = 234 variance_[i] =
235 (NewMean(running_mean_sq_[i], sub_running_mean_sq_[i], blocks) - 235 (NewMean(running_mean_sq_[i], sub_running_mean_sq_[i], blocks) -
236 NewMean(running_mean_[i], sub_running_mean_[i], blocks) * 236 NewMean(running_mean_[i], sub_running_mean_[i], blocks) *
237 std::conj(NewMean(running_mean_[i], sub_running_mean_[i], blocks))) 237 std::conj(NewMean(running_mean_[i], sub_running_mean_[i], blocks)))
238 .real(); 238 .real();
239 if (count_ == kWindowBlockSize - 1) { 239 if (count_ == kWindowBlockSize - 1) {
240 sub_running_mean_[i] = complex<float>(0.0f, 0.0f); 240 sub_running_mean_[i] = complex<float>(0.0f, 0.0f);
241 sub_running_mean_sq_[i] = complex<float>(0.0f, 0.0f); 241 sub_running_mean_sq_[i] = complex<float>(0.0f, 0.0f);
242 running_mean_[i] = complex<float>(0.0f, 0.0f); 242 running_mean_[i] = complex<float>(0.0f, 0.0f);
243 running_mean_sq_[i] = complex<float>(0.0f, 0.0f); 243 running_mean_sq_[i] = complex<float>(0.0f, 0.0f);
244 for (int j = 0; j < min(window_size_, history_cursor_); ++j) { 244 for (int j = 0; j < min(window_size_, history_cursor_); ++j) {
245 AddToMean(subhistory_[i][j], j, &running_mean_[i]); 245 AddToMean(subhistory_[i][j], j + 1, &running_mean_[i]);
246 AddToMean(subhistory_sq_[i][j], j, &running_mean_sq_[i]); 246 AddToMean(subhistory_sq_[i][j], j + 1, &running_mean_sq_[i]);
247 } 247 }
248 ++history_cursor_; 248 ++history_cursor_;
249 } 249 }
250 } 250 }
251 ++count_; 251 ++count_;
252 if (count_ == kWindowBlockSize) { 252 if (count_ == kWindowBlockSize) {
253 count_ = 0; 253 count_ = 0;
254 } 254 }
255 } 255 }
256 256
257 // Recomputes variances from scratch each window based on previous window.
hlundin-webrtc 2015/06/30 14:00:53 *for* each window?
ekm 2015/07/01 23:48:26 Done.
258 void VarianceArray::BlockBasedMovingAverage(
259 const std::complex<float>* data, bool /*dummy*/) {
260 // TODO(ekmeyerson) To mitigate potential divergence, add counter so that
261 // after every so often sums are computed scratch by summing over all
262 // elements instead of subtracting oldest and adding newest.
263 for (int i = 0; i < freqs_; ++i) {
264 sub_running_mean_[i] += data[i];
265 sub_running_mean_sq_[i] += data[i] * std::conj(data[i]);
266 }
267 ++count_;
268
269 // TODO(ekmeyerson) Make kWindowBlockSize nonconstant to allow
270 // experimentation with different block size,window size pairs.
271 if (count_ >= kWindowBlockSize) {
272 count_ = 0;
273
274 for (int i = 0; i < freqs_; ++i) {
275 running_mean_[i] -= subhistory_[i][history_cursor_];
276 running_mean_sq_[i] -= subhistory_sq_[i][history_cursor_];
277
278 float scale = 1.f / kWindowBlockSize;
279 subhistory_[i][history_cursor_] = sub_running_mean_[i] * scale;
280 subhistory_sq_[i][history_cursor_] = sub_running_mean_sq_[i] * scale;
281
282 sub_running_mean_[i] = std::complex<float>(0.0f, 0.0f);
283 sub_running_mean_sq_[i] = std::complex<float>(0.0f, 0.0f);
284
285 running_mean_[i] += subhistory_[i][history_cursor_];
286 running_mean_sq_[i] += subhistory_sq_[i][history_cursor_];
287
288 scale = 1.f / (buffer_full_ ? window_size_ : history_cursor_ + 1);
289 variance_[i] = std::real(running_mean_sq_[i] * scale - running_mean_[i] *
290 scale * std::conj(running_mean_[i]) * scale);
291 }
292
293 ++history_cursor_;
294 if (history_cursor_ >= window_size_) {
295 buffer_full_ = true;
296 history_cursor_ = 0;
297 }
298 }
299 }
300
257 void VarianceArray::Clear() { 301 void VarianceArray::Clear() {
258 memset(running_mean_.get(), 0, sizeof(*running_mean_.get()) * freqs_); 302 memset(running_mean_.get(), 0, sizeof(*running_mean_.get()) * freqs_);
259 memset(running_mean_sq_.get(), 0, sizeof(*running_mean_sq_.get()) * freqs_); 303 memset(running_mean_sq_.get(), 0, sizeof(*running_mean_sq_.get()) * freqs_);
260 memset(variance_.get(), 0, sizeof(*variance_.get()) * freqs_); 304 memset(variance_.get(), 0, sizeof(*variance_.get()) * freqs_);
261 memset(conj_sum_.get(), 0, sizeof(*conj_sum_.get()) * freqs_); 305 memset(conj_sum_.get(), 0, sizeof(*conj_sum_.get()) * freqs_);
262 history_cursor_ = 0; 306 history_cursor_ = 0;
263 count_ = 0; 307 count_ = 0;
264 array_mean_ = 0.0f; 308 array_mean_ = 0.0f;
265 } 309 }
266 310
(...skipping 24 matching lines...) Expand all
291 factor = 1.0f; 335 factor = 1.0f;
292 } 336 }
293 out_block[i] = factor * in_block[i]; 337 out_block[i] = factor * in_block[i];
294 current_[i] = UpdateFactor(target_[i], current_[i], change_limit_); 338 current_[i] = UpdateFactor(target_[i], current_[i], change_limit_);
295 } 339 }
296 } 340 }
297 341
298 } // namespace intelligibility 342 } // namespace intelligibility
299 343
300 } // namespace webrtc 344 } // namespace webrtc
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698