Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 /* | |
| 2 * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. | |
| 3 * | |
| 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 | |
| 6 * tree. An additional intellectual property rights grant can be found | |
| 7 * in the file PATENTS. All contributing project authors may | |
| 8 * be found in the AUTHORS file in the root of the source tree. | |
| 9 */ | |
| 10 | |
| 11 #include <deque> | |
| 12 #include <vector> | |
| 13 | |
| 14 #include "webrtc/modules/audio_processing/aec3/aec3_constants.h" | |
| 15 #include "webrtc/modules/audio_processing/aec3/block_framer.h" | |
| 16 #include "webrtc/modules/audio_processing/aec3/block_processor.h" | |
| 17 #include "webrtc/modules/audio_processing/aec3/echo_canceller3.h" | |
| 18 #include "webrtc/modules/audio_processing/aec3/frame_blocker.h" | |
| 19 #include "webrtc/modules/audio_processing/audio_buffer.h" | |
| 20 #include "webrtc/test/gtest.h" | |
| 21 | |
| 22 namespace webrtc { | |
| 23 namespace { | |
| 24 void PopulateInputFrame(size_t frame_length, | |
|
hlundin-webrtc
2016/12/20 15:10:35
Please, comment on how the frame is filled. What v
peah-webrtc
2016/12/21 23:13:53
Done.
| |
| 25 size_t num_bands, | |
| 26 size_t frame_index, | |
| 27 float* const* frame, | |
| 28 int offset) { | |
| 29 for (size_t k = 0; k < num_bands; ++k) { | |
| 30 for (size_t i = 0; i < frame_length; ++i) { | |
| 31 float value = static_cast<int>(frame_index * frame_length + i) + offset; | |
| 32 frame[k][i] = (value > 0 ? 5000 * k + value : 0); | |
| 33 } | |
| 34 } | |
| 35 } | |
| 36 | |
| 37 bool VerifyInputFrame(size_t frame_length, | |
| 38 size_t num_bands, | |
| 39 size_t frame_index, | |
| 40 float* const* frame, | |
|
hlundin-webrtc
2016/12/20 15:10:35
More const.
peah-webrtc
2016/12/21 23:13:51
Done.
| |
| 41 int offset) { | |
| 42 float reference_frame_data[num_bands][frame_length]; | |
| 43 float* reference_frame[num_bands]; | |
| 44 for (size_t k = 0; k < num_bands; ++k) { | |
| 45 reference_frame[k] = &reference_frame_data[k][0]; | |
| 46 } | |
| 47 | |
| 48 PopulateInputFrame(frame_length, num_bands, frame_index, reference_frame, | |
| 49 offset); | |
| 50 for (size_t k = 0; k < num_bands; ++k) { | |
| 51 for (size_t i = 0; i < frame_length; ++i) { | |
| 52 if (reference_frame[k][i] != frame[k][i]) { | |
| 53 return false; | |
| 54 } | |
| 55 } | |
| 56 } | |
| 57 | |
| 58 return true; | |
| 59 } | |
| 60 | |
| 61 void FillSubFrameView( | |
| 62 const float* const* frame, | |
| 63 size_t sub_frame_index, | |
| 64 rtc::ArrayView<rtc::ArrayView<const float>> sub_frame_view) { | |
| 65 for (size_t k = 0; k < sub_frame_view.size(); ++k) { | |
| 66 sub_frame_view[k] = rtc::ArrayView<const float>( | |
| 67 &frame[k][sub_frame_index * kSubFrameLength], kSubFrameLength); | |
| 68 } | |
| 69 } | |
| 70 | |
| 71 void FillSubFrameView(float* const* frame, | |
| 72 size_t sub_frame_index, | |
| 73 rtc::ArrayView<rtc::ArrayView<float>> sub_frame_view) { | |
| 74 for (size_t k = 0; k < sub_frame_view.size(); ++k) { | |
| 75 sub_frame_view[k] = rtc::ArrayView<float>( | |
| 76 &frame[k][sub_frame_index * kSubFrameLength], kSubFrameLength); | |
| 77 } | |
| 78 } | |
| 79 | |
| 80 // Class for testing that echo leakage is properly reported to the | |
|
hlundin-webrtc
2016/12/20 15:10:35
This is not used for the next 400 lines. Define th
peah-webrtc
2016/12/21 23:13:52
Done.
| |
| 81 // BlockProcessor. | |
| 82 class BlockProcessorEchoLeakageReport : public BlockProcessor { | |
| 83 public: | |
| 84 explicit BlockProcessorEchoLeakageReport(size_t num_bands); | |
| 85 ~BlockProcessorEchoLeakageReport() override; | |
| 86 | |
| 87 void ProcessCapture(bool known_echo_path_change, | |
| 88 bool saturated_microphone_signal, | |
| 89 std::vector<std::vector<float>>* capture_block) override; | |
| 90 | |
| 91 bool BufferRender(std::vector<std::vector<float>>* block) override; | |
| 92 | |
| 93 void ReportEchoLeakage(bool leakage_detected) override; | |
| 94 | |
| 95 private: | |
| 96 float echo_leakage_indicator_ = 0.f; | |
| 97 RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(BlockProcessorEchoLeakageReport); | |
| 98 }; | |
| 99 | |
| 100 BlockProcessorEchoLeakageReport::BlockProcessorEchoLeakageReport( | |
|
hlundin-webrtc
2016/12/20 15:10:36
Merge definitions into the class declaration above
peah-webrtc
2016/12/21 23:13:52
Done.
| |
| 101 size_t num_bands) {} | |
| 102 | |
| 103 BlockProcessorEchoLeakageReport::~BlockProcessorEchoLeakageReport() = default; | |
| 104 | |
| 105 void BlockProcessorEchoLeakageReport::ProcessCapture( | |
| 106 bool known_echo_path_change, | |
| 107 bool saturated_microphone_signal, | |
| 108 std::vector<std::vector<float>>* capture_block) { | |
| 109 for (auto& c : (*capture_block)[0]) { | |
| 110 c = echo_leakage_indicator_; | |
| 111 } | |
| 112 } | |
| 113 | |
| 114 bool BlockProcessorEchoLeakageReport::BufferRender( | |
| 115 std::vector<std::vector<float>>* block) { | |
| 116 return false; | |
| 117 } | |
| 118 | |
| 119 void BlockProcessorEchoLeakageReport::ReportEchoLeakage(bool leakage_detected) { | |
| 120 echo_leakage_indicator_ = leakage_detected ? 1.f : 0.f; | |
| 121 } | |
| 122 | |
| 123 // Class for testing that echo leakage is properly reported to the | |
|
hlundin-webrtc
2016/12/20 15:10:35
Copy-paste failure with the comment.
peah-webrtc
2016/12/21 23:13:52
Done.
| |
| 124 // BlockProcessor. | |
| 125 class BlockProcessorCaptureSaturationReport : public BlockProcessor { | |
| 126 public: | |
| 127 explicit BlockProcessorCaptureSaturationReport(size_t num_bands); | |
| 128 ~BlockProcessorCaptureSaturationReport() override; | |
| 129 | |
| 130 void ProcessCapture(bool known_echo_path_change, | |
| 131 bool saturated_microphone_signal, | |
| 132 std::vector<std::vector<float>>* capture_block) override; | |
| 133 | |
| 134 bool BufferRender(std::vector<std::vector<float>>* block) override; | |
| 135 | |
| 136 void ReportEchoLeakage(bool leakage_detected) override; | |
| 137 | |
| 138 private: | |
| 139 RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(BlockProcessorCaptureSaturationReport); | |
| 140 }; | |
| 141 | |
| 142 BlockProcessorCaptureSaturationReport::BlockProcessorCaptureSaturationReport( | |
| 143 size_t num_bands) {} | |
| 144 | |
| 145 BlockProcessorCaptureSaturationReport:: | |
| 146 ~BlockProcessorCaptureSaturationReport() = default; | |
| 147 | |
| 148 void BlockProcessorCaptureSaturationReport::ProcessCapture( | |
| 149 bool known_echo_path_change, | |
| 150 bool saturated_microphone_signal, | |
| 151 std::vector<std::vector<float>>* capture_block) { | |
| 152 for (auto& c : (*capture_block)[0]) { | |
| 153 c = saturated_microphone_signal ? 1 : 0; | |
|
aleloi
2016/12/21 10:07:45
Nit: 1.f, 0.f for consistency.
peah-webrtc
2016/12/21 23:13:52
Done.
| |
| 154 } | |
| 155 } | |
| 156 | |
| 157 bool BlockProcessorCaptureSaturationReport::BufferRender( | |
| 158 std::vector<std::vector<float>>* block) { | |
| 159 return false; | |
| 160 } | |
| 161 | |
| 162 void BlockProcessorCaptureSaturationReport::ReportEchoLeakage( | |
| 163 bool leakage_detected) {} | |
| 164 | |
| 165 // Class for testing that a known echo path change is properly reported to the | |
| 166 // BlockProcessor. | |
| 167 class BlockProcessorKnownEchoPathChangeReport : public BlockProcessor { | |
| 168 public: | |
| 169 explicit BlockProcessorKnownEchoPathChangeReport(size_t num_bands); | |
| 170 ~BlockProcessorKnownEchoPathChangeReport() override; | |
| 171 | |
| 172 void ProcessCapture(bool known_echo_path_change, | |
| 173 bool saturated_microphone_signal, | |
| 174 std::vector<std::vector<float>>* capture_block) override; | |
| 175 | |
| 176 bool BufferRender(std::vector<std::vector<float>>* block) override; | |
| 177 | |
| 178 void ReportEchoLeakage(bool leakage_detected) override; | |
| 179 | |
| 180 private: | |
| 181 RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(BlockProcessorKnownEchoPathChangeReport); | |
| 182 }; | |
| 183 | |
| 184 BlockProcessorKnownEchoPathChangeReport:: | |
| 185 BlockProcessorKnownEchoPathChangeReport(size_t num_bands) {} | |
| 186 | |
| 187 BlockProcessorKnownEchoPathChangeReport:: | |
| 188 ~BlockProcessorKnownEchoPathChangeReport() = default; | |
| 189 | |
| 190 void BlockProcessorKnownEchoPathChangeReport::ProcessCapture( | |
| 191 bool known_echo_path_change, | |
| 192 bool saturated_microphone_signal, | |
| 193 std::vector<std::vector<float>>* capture_block) { | |
| 194 for (auto& c : (*capture_block)[0]) { | |
| 195 c = known_echo_path_change ? 1 : 0; | |
|
aleloi
2016/12/21 10:07:45
nit: 1.f, 0.f
peah-webrtc
2016/12/21 23:13:52
Done.
| |
| 196 } | |
| 197 } | |
| 198 | |
| 199 bool BlockProcessorKnownEchoPathChangeReport::BufferRender( | |
| 200 std::vector<std::vector<float>>* block) { | |
| 201 return false; | |
| 202 } | |
| 203 | |
| 204 void BlockProcessorKnownEchoPathChangeReport::ReportEchoLeakage( | |
| 205 bool leakage_detected) {} | |
| 206 | |
| 207 // Class for testing that the render data is properly received by the block | |
| 208 // processor. | |
| 209 class BlockProcessorRenderRedirect : public BlockProcessor { | |
|
hlundin-webrtc
2016/12/20 15:10:35
Move closer to where it is used.
peah-webrtc
2016/12/21 23:13:52
Done.
| |
| 210 public: | |
| 211 explicit BlockProcessorRenderRedirect(size_t num_bands); | |
| 212 ~BlockProcessorRenderRedirect() override; | |
| 213 | |
| 214 void ProcessCapture(bool known_echo_path_change, | |
| 215 bool saturated_microphone_signal, | |
| 216 std::vector<std::vector<float>>* capture_block) override; | |
| 217 | |
| 218 bool BufferRender(std::vector<std::vector<float>>* block) override; | |
| 219 | |
| 220 void ReportEchoLeakage(bool leakage_detected) override; | |
| 221 | |
| 222 private: | |
| 223 std::deque<std::vector<std::vector<float>>> received_render_blocks_; | |
| 224 RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(BlockProcessorRenderRedirect); | |
| 225 }; | |
| 226 | |
| 227 BlockProcessorRenderRedirect::BlockProcessorRenderRedirect(size_t num_bands) {} | |
|
hlundin-webrtc
2016/12/20 15:10:35
Merge definitions into class declaration.
peah-webrtc
2016/12/21 23:13:52
Done.
| |
| 228 | |
| 229 BlockProcessorRenderRedirect::~BlockProcessorRenderRedirect() = default; | |
| 230 | |
| 231 void BlockProcessorRenderRedirect::ProcessCapture( | |
| 232 bool known_echo_path_change, | |
| 233 bool saturated_microphone_signal, | |
| 234 std::vector<std::vector<float>>* capture_block) { | |
| 235 std::vector<std::vector<float>> render_block = | |
| 236 received_render_blocks_.front(); | |
| 237 received_render_blocks_.pop_front(); | |
| 238 capture_block->swap(render_block); | |
| 239 } | |
| 240 | |
| 241 bool BlockProcessorRenderRedirect::BufferRender( | |
| 242 std::vector<std::vector<float>>* block) { | |
| 243 received_render_blocks_.push_back(*block); | |
| 244 return false; | |
| 245 } | |
| 246 | |
| 247 void BlockProcessorRenderRedirect::ReportEchoLeakage(bool leakage_detected) {} | |
| 248 | |
| 249 // Class for testing that the capture data is properly received by the block | |
| 250 // processor. | |
| 251 class BlockProcessorTransparentCapture : public BlockProcessor { | |
|
ivoc
2016/12/21 14:52:13
Is it me or does this class do nothing? Why is it
peah-webrtc
2016/12/21 23:13:52
It is needed to ensure that the capture signal is
ivoc
2016/12/22 13:38:13
Acknowledged.
peah-webrtc
2017/01/02 08:45:10
Acknowledged.
| |
| 252 public: | |
| 253 explicit BlockProcessorTransparentCapture(size_t num_bands); | |
| 254 ~BlockProcessorTransparentCapture() override; | |
| 255 | |
| 256 void ProcessCapture(bool known_echo_path_change, | |
| 257 bool saturated_microphone_signal, | |
| 258 std::vector<std::vector<float>>* capture_block) override; | |
| 259 | |
| 260 bool BufferRender(std::vector<std::vector<float>>* block) override; | |
| 261 | |
| 262 void ReportEchoLeakage(bool leakage_detected) override; | |
| 263 | |
| 264 private: | |
| 265 RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(BlockProcessorTransparentCapture); | |
| 266 }; | |
| 267 | |
| 268 BlockProcessorTransparentCapture::BlockProcessorTransparentCapture( | |
|
hlundin-webrtc
2016/12/20 15:10:35
Merge definitions into class declaration.
peah-webrtc
2016/12/21 23:13:52
Done.
| |
| 269 size_t num_bands) {} | |
| 270 | |
| 271 BlockProcessorTransparentCapture::~BlockProcessorTransparentCapture() = default; | |
| 272 | |
| 273 void BlockProcessorTransparentCapture::ProcessCapture( | |
| 274 bool known_echo_path_change, | |
| 275 bool saturated_microphone_signal, | |
| 276 std::vector<std::vector<float>>* capture_block) {} | |
| 277 | |
| 278 bool BlockProcessorTransparentCapture::BufferRender( | |
| 279 std::vector<std::vector<float>>* block) { | |
| 280 return false; | |
| 281 } | |
| 282 | |
| 283 void BlockProcessorTransparentCapture::ReportEchoLeakage( | |
| 284 bool leakage_detected) {} | |
| 285 | |
| 286 void RunCapturePipelineVerificationTest(int sample_rate_hz) { | |
|
hlundin-webrtc
2016/12/20 15:10:35
Write a comment about what the test actually tests
peah-webrtc
2016/12/21 23:13:53
Done.
| |
| 287 const size_t num_bands = NumBandsForRate(sample_rate_hz); | |
|
hlundin-webrtc
2016/12/20 15:10:35
You are duplicating these 10-20 lines an awful lot
peah-webrtc
2016/12/21 23:13:52
I made a tester class that unifies the common part
hlundin-webrtc
2016/12/22 10:23:56
OK. That's _almost_ a test fixture then... :)
peah-webrtc
2017/01/02 08:45:10
:-) Yes, but more explicit.
| |
| 288 const size_t frame_length = sample_rate_hz == 8000 ? 80 : 160; | |
| 289 float input_frame_data[num_bands][frame_length]; | |
| 290 float* input_frame[num_bands]; | |
| 291 float output_frame_data[num_bands][frame_length]; | |
| 292 float* output_frame[num_bands]; | |
| 293 for (size_t k = 0; k < num_bands; ++k) { | |
| 294 input_frame[k] = &input_frame_data[k][0]; | |
| 295 output_frame[k] = &output_frame_data[k][0]; | |
| 296 } | |
| 297 | |
| 298 const int samples_per_channel = rtc::CheckedDivExact(sample_rate_hz, 100); | |
| 299 AudioBuffer capture_buffer(samples_per_channel, 1, samples_per_channel, 1, | |
| 300 samples_per_channel); | |
| 301 AudioBuffer render_buffer(samples_per_channel, 1, samples_per_channel, 1, | |
| 302 samples_per_channel); | |
| 303 | |
| 304 EchoCanceller3 aec3(sample_rate_hz, false, | |
| 305 new BlockProcessorTransparentCapture(num_bands)); | |
| 306 | |
| 307 for (size_t frame_index = 0; frame_index < 20; ++frame_index) { | |
| 308 aec3.AnalyzeCapture(&capture_buffer); | |
| 309 if (sample_rate_hz > 16000) { | |
| 310 capture_buffer.SplitIntoFrequencyBands(); | |
| 311 render_buffer.SplitIntoFrequencyBands(); | |
| 312 } | |
| 313 | |
| 314 PopulateInputFrame(frame_length, num_bands, frame_index, | |
| 315 &capture_buffer.split_bands_f(0)[0], 0); | |
| 316 PopulateInputFrame(frame_length, num_bands, frame_index, | |
| 317 &render_buffer.split_bands_f(0)[0], 100); | |
| 318 | |
| 319 EXPECT_TRUE(aec3.AnalyzeRender(&render_buffer)); | |
| 320 aec3.ProcessCapture(&capture_buffer, false); | |
| 321 EXPECT_TRUE(VerifyInputFrame(frame_length, num_bands, frame_index, | |
| 322 &capture_buffer.split_bands_f(0)[0], -64)); | |
| 323 } | |
| 324 } | |
| 325 | |
| 326 void RunRenderPipelineVerificationTest(int sample_rate_hz) { | |
| 327 const size_t num_bands = NumBandsForRate(sample_rate_hz); | |
| 328 const size_t frame_length = sample_rate_hz == 8000 ? 80 : 160; | |
| 329 float input_frame_data[num_bands][frame_length]; | |
| 330 float* input_frame[num_bands]; | |
| 331 float output_frame_data[num_bands][frame_length]; | |
| 332 float* output_frame[num_bands]; | |
| 333 for (size_t k = 0; k < num_bands; ++k) { | |
| 334 input_frame[k] = &input_frame_data[k][0]; | |
| 335 output_frame[k] = &output_frame_data[k][0]; | |
| 336 } | |
| 337 | |
| 338 const int samples_per_channel = rtc::CheckedDivExact(sample_rate_hz, 100); | |
| 339 AudioBuffer capture_buffer(samples_per_channel, 1, samples_per_channel, 1, | |
| 340 samples_per_channel); | |
| 341 AudioBuffer render_buffer(samples_per_channel, 1, samples_per_channel, 1, | |
| 342 samples_per_channel); | |
| 343 | |
| 344 EchoCanceller3 aec3(sample_rate_hz, false, | |
| 345 new BlockProcessorRenderRedirect(num_bands)); | |
| 346 | |
| 347 for (size_t frame_index = 0; frame_index < 20; ++frame_index) { | |
| 348 aec3.AnalyzeCapture(&capture_buffer); | |
| 349 if (sample_rate_hz > 16000) { | |
| 350 capture_buffer.SplitIntoFrequencyBands(); | |
| 351 render_buffer.SplitIntoFrequencyBands(); | |
| 352 } | |
| 353 | |
| 354 PopulateInputFrame(frame_length, num_bands, frame_index, | |
| 355 &capture_buffer.split_bands_f(0)[0], 100); | |
| 356 PopulateInputFrame(frame_length, num_bands, frame_index, | |
| 357 &render_buffer.split_bands_f(0)[0], 0); | |
| 358 | |
| 359 EXPECT_TRUE(aec3.AnalyzeRender(&render_buffer)); | |
| 360 aec3.ProcessCapture(&capture_buffer, false); | |
| 361 EXPECT_TRUE(VerifyInputFrame(frame_length, num_bands, frame_index, | |
| 362 &capture_buffer.split_bands_f(0)[0], -64)); | |
| 363 } | |
| 364 } | |
| 365 | |
| 366 enum class EchoPathTestVariant { kNone, kOneSticky, kOneNonSticky }; | |
|
ivoc
2016/12/21 14:52:13
Please add a comment to explain what each variant
peah-webrtc
2016/12/21 23:13:52
Done.
| |
| 367 | |
| 368 void RunEchoPathChangeVerificationTest( | |
| 369 int sample_rate_hz, | |
| 370 EchoPathTestVariant echo_path_change_test_variant) { | |
| 371 const size_t num_bands = NumBandsForRate(sample_rate_hz); | |
| 372 const size_t frame_length = sample_rate_hz == 8000 ? 80 : 160; | |
| 373 float input_frame_data[num_bands][frame_length]; | |
| 374 float* input_frame[num_bands]; | |
| 375 float output_frame_data[num_bands][frame_length]; | |
| 376 float* output_frame[num_bands]; | |
| 377 for (size_t k = 0; k < num_bands; ++k) { | |
| 378 input_frame[k] = &input_frame_data[k][0]; | |
| 379 output_frame[k] = &output_frame_data[k][0]; | |
| 380 } | |
| 381 | |
| 382 const int samples_per_channel = rtc::CheckedDivExact(sample_rate_hz, 100); | |
| 383 AudioBuffer capture_buffer(samples_per_channel, 1, samples_per_channel, 1, | |
| 384 samples_per_channel); | |
| 385 AudioBuffer render_buffer(samples_per_channel, 1, samples_per_channel, 1, | |
| 386 samples_per_channel); | |
| 387 | |
| 388 EchoCanceller3 aec3(sample_rate_hz, false, | |
| 389 new BlockProcessorKnownEchoPathChangeReport(num_bands)); | |
|
hlundin-webrtc
2016/12/20 15:10:36
Use a mock instead to verify that BlockProcessor::
peah-webrtc
2016/12/21 23:13:52
Done.
| |
| 390 | |
| 391 for (size_t frame_index = 0; frame_index < 20; ++frame_index) { | |
| 392 bool echo_path_change = false; | |
| 393 switch (echo_path_change_test_variant) { | |
| 394 case EchoPathTestVariant::kNone: | |
| 395 break; | |
| 396 case EchoPathTestVariant::kOneSticky: | |
| 397 echo_path_change = true; | |
| 398 break; | |
| 399 case EchoPathTestVariant::kOneNonSticky: | |
| 400 if (frame_index == 0) { | |
| 401 echo_path_change = true; | |
| 402 } | |
| 403 break; | |
| 404 default: | |
|
hlundin-webrtc
2016/12/20 15:10:35
You can omit the default case. Since you are switc
peah-webrtc
2016/12/21 23:13:52
Great! Thanks!
Done.
| |
| 405 RTC_NOTREACHED(); | |
| 406 } | |
| 407 | |
| 408 aec3.AnalyzeCapture(&capture_buffer); | |
| 409 if (sample_rate_hz > 16000) { | |
| 410 capture_buffer.SplitIntoFrequencyBands(); | |
| 411 render_buffer.SplitIntoFrequencyBands(); | |
| 412 } | |
| 413 | |
| 414 PopulateInputFrame(frame_length, num_bands, frame_index, | |
| 415 &capture_buffer.split_bands_f(0)[0], 100); | |
| 416 PopulateInputFrame(frame_length, num_bands, frame_index, | |
| 417 &render_buffer.split_bands_f(0)[0], 0); | |
| 418 | |
| 419 EXPECT_TRUE(aec3.AnalyzeRender(&render_buffer)); | |
| 420 aec3.ProcessCapture(&capture_buffer, echo_path_change); | |
| 421 | |
| 422 switch (echo_path_change_test_variant) { | |
| 423 case EchoPathTestVariant::kNone: | |
| 424 EXPECT_EQ(0.f, capture_buffer.split_bands_f(0)[0][0]); | |
| 425 break; | |
| 426 case EchoPathTestVariant::kOneSticky: | |
| 427 if (frame_index > 0) { | |
| 428 EXPECT_EQ(1.f, capture_buffer.split_bands_f(0)[0][0]); | |
| 429 } | |
| 430 break; | |
| 431 case EchoPathTestVariant::kOneNonSticky: | |
| 432 if (frame_index > 10) { | |
| 433 EXPECT_EQ(0.f, capture_buffer.split_bands_f(0)[0][0]); | |
| 434 } | |
| 435 break; | |
| 436 default: | |
|
hlundin-webrtc
2016/12/20 15:10:35
Remove default.
peah-webrtc
2016/12/21 23:13:53
Done.
| |
| 437 RTC_NOTREACHED(); | |
| 438 } | |
| 439 } | |
| 440 } | |
| 441 | |
| 442 enum class EchoLeakageTestVariant { | |
|
ivoc
2016/12/21 14:52:13
Please add a comment here as well to explain the v
peah-webrtc
2016/12/21 23:13:52
Done.
| |
| 443 kNone, | |
| 444 kFalseSticky, | |
| 445 kTrueSticky, | |
| 446 kTrueNonSticky | |
| 447 }; | |
| 448 | |
| 449 void RunEchoLeakageVerificationTest( | |
| 450 int sample_rate_hz, | |
| 451 EchoLeakageTestVariant leakage_report_variant) { | |
| 452 const size_t num_bands = NumBandsForRate(sample_rate_hz); | |
| 453 const size_t frame_length = sample_rate_hz == 8000 ? 80 : 160; | |
| 454 float input_frame_data[num_bands][frame_length]; | |
| 455 float* input_frame[num_bands]; | |
| 456 float output_frame_data[num_bands][frame_length]; | |
| 457 float* output_frame[num_bands]; | |
| 458 for (size_t k = 0; k < num_bands; ++k) { | |
| 459 input_frame[k] = &input_frame_data[k][0]; | |
| 460 output_frame[k] = &output_frame_data[k][0]; | |
| 461 } | |
| 462 | |
| 463 const int samples_per_channel = rtc::CheckedDivExact(sample_rate_hz, 100); | |
| 464 AudioBuffer capture_buffer(samples_per_channel, 1, samples_per_channel, 1, | |
| 465 samples_per_channel); | |
| 466 AudioBuffer render_buffer(samples_per_channel, 1, samples_per_channel, 1, | |
| 467 samples_per_channel); | |
| 468 | |
| 469 EchoCanceller3 aec3(sample_rate_hz, false, | |
| 470 new BlockProcessorEchoLeakageReport(num_bands)); | |
|
hlundin-webrtc
2016/12/20 15:10:35
Rewrite this test to use a mock instead of BlockPr
peah-webrtc
2016/12/21 23:13:52
Done.
| |
| 471 | |
| 472 for (size_t frame_index = 0; frame_index < 20; ++frame_index) { | |
| 473 switch (leakage_report_variant) { | |
| 474 case EchoLeakageTestVariant::kNone: | |
| 475 break; | |
| 476 case EchoLeakageTestVariant::kFalseSticky: | |
| 477 if (frame_index == 0) { | |
| 478 aec3.ReportEchoLeakage(false); | |
| 479 } | |
| 480 break; | |
| 481 case EchoLeakageTestVariant::kTrueSticky: | |
| 482 if (frame_index == 0) { | |
| 483 aec3.ReportEchoLeakage(true); | |
| 484 } | |
| 485 break; | |
| 486 case EchoLeakageTestVariant::kTrueNonSticky: | |
| 487 if (frame_index == 0) { | |
| 488 aec3.ReportEchoLeakage(true); | |
| 489 } else { | |
| 490 aec3.ReportEchoLeakage(false); | |
| 491 } | |
| 492 | |
| 493 break; | |
| 494 default: | |
|
hlundin-webrtc
2016/12/20 15:10:35
Remove default.
peah-webrtc
2016/12/21 23:13:52
Done.
| |
| 495 RTC_NOTREACHED(); | |
| 496 } | |
| 497 | |
| 498 aec3.AnalyzeCapture(&capture_buffer); | |
| 499 if (sample_rate_hz > 16000) { | |
| 500 capture_buffer.SplitIntoFrequencyBands(); | |
| 501 render_buffer.SplitIntoFrequencyBands(); | |
| 502 } | |
| 503 | |
| 504 PopulateInputFrame(frame_length, num_bands, frame_index, | |
| 505 &capture_buffer.split_bands_f(0)[0], 100); | |
| 506 PopulateInputFrame(frame_length, num_bands, frame_index, | |
| 507 &render_buffer.split_bands_f(0)[0], 0); | |
| 508 | |
| 509 EXPECT_TRUE(aec3.AnalyzeRender(&render_buffer)); | |
| 510 aec3.ProcessCapture(&capture_buffer, false); | |
| 511 | |
| 512 switch (leakage_report_variant) { | |
| 513 case EchoLeakageTestVariant::kNone: | |
| 514 EXPECT_EQ(0.f, capture_buffer.split_bands_f(0)[0][0]); | |
| 515 break; | |
| 516 case EchoLeakageTestVariant::kFalseSticky: | |
| 517 EXPECT_EQ(0.f, capture_buffer.split_bands_f(0)[0][0]); | |
| 518 break; | |
| 519 case EchoLeakageTestVariant::kTrueSticky: | |
| 520 if (frame_index > 0) { | |
| 521 EXPECT_EQ(1.f, capture_buffer.split_bands_f(0)[0][0]); | |
| 522 } | |
| 523 break; | |
| 524 case EchoLeakageTestVariant::kTrueNonSticky: | |
| 525 if (frame_index == 0) { | |
| 526 EXPECT_EQ(0.f, capture_buffer.split_bands_f(0)[0][0]); | |
| 527 } else if (frame_index == 1) { | |
| 528 EXPECT_EQ(1.f, capture_buffer.split_bands_f(0)[0][0]); | |
| 529 } else if (frame_index > 2) { | |
| 530 EXPECT_EQ(0.f, capture_buffer.split_bands_f(0)[0][0]); | |
| 531 } | |
| 532 break; | |
| 533 default: | |
|
hlundin-webrtc
2016/12/20 15:10:36
Remove default.
peah-webrtc
2016/12/21 23:13:52
Done.
| |
| 534 RTC_NOTREACHED(); | |
| 535 } | |
| 536 } | |
| 537 } | |
| 538 | |
| 539 enum class SaturationTestVariant { | |
|
ivoc
2016/12/21 14:52:13
Comment about variants, please.
peah-webrtc
2016/12/21 23:13:53
Done.
| |
| 540 kNone, | |
| 541 kOneNegative, | |
| 542 kOnePositive, | |
| 543 kNotSticky | |
| 544 }; | |
| 545 | |
| 546 void RunCaptureSaturationVerificationTest( | |
| 547 int sample_rate_hz, | |
| 548 SaturationTestVariant saturation_variant) { | |
| 549 const size_t num_bands = NumBandsForRate(sample_rate_hz); | |
| 550 const size_t frame_length = sample_rate_hz == 8000 ? 80 : 160; | |
| 551 float input_frame_data[num_bands][frame_length]; | |
| 552 float* input_frame[num_bands]; | |
| 553 float output_frame_data[num_bands][frame_length]; | |
| 554 float* output_frame[num_bands]; | |
| 555 for (size_t k = 0; k < num_bands; ++k) { | |
| 556 input_frame[k] = &input_frame_data[k][0]; | |
| 557 output_frame[k] = &output_frame_data[k][0]; | |
| 558 } | |
| 559 | |
| 560 const int samples_per_channel = rtc::CheckedDivExact(sample_rate_hz, 100); | |
| 561 AudioBuffer capture_buffer(samples_per_channel, 1, samples_per_channel, 1, | |
| 562 samples_per_channel); | |
| 563 AudioBuffer render_buffer(samples_per_channel, 1, samples_per_channel, 1, | |
| 564 samples_per_channel); | |
| 565 | |
| 566 EchoCanceller3 aec3(sample_rate_hz, false, | |
| 567 new BlockProcessorCaptureSaturationReport(num_bands)); | |
|
hlundin-webrtc
2016/12/20 15:10:35
Use a mock instead to verify that BlockProcessor::
peah-webrtc
2016/12/21 23:13:51
Done.
| |
| 568 | |
| 569 for (size_t frame_index = 0; frame_index < 20; ++frame_index) { | |
| 570 for (int k = 0; k < samples_per_channel; ++k) { | |
| 571 capture_buffer.channels_f()[0][k] = 0.f; | |
| 572 } | |
| 573 switch (saturation_variant) { | |
| 574 case SaturationTestVariant::kNone: | |
| 575 break; | |
| 576 case SaturationTestVariant::kOneNegative: | |
| 577 capture_buffer.channels_f()[0][10] = -32768.f; | |
| 578 break; | |
| 579 case SaturationTestVariant::kOnePositive: | |
| 580 capture_buffer.channels_f()[0][10] = 32767.f; | |
| 581 break; | |
| 582 case SaturationTestVariant::kNotSticky: | |
| 583 if (frame_index < 14) { | |
| 584 capture_buffer.channels_f()[0][10] = 32767.f; | |
| 585 } | |
| 586 break; | |
| 587 default: | |
|
hlundin-webrtc
2016/12/20 15:10:36
Remove default.
peah-webrtc
2016/12/21 23:13:52
Done.
| |
| 588 RTC_NOTREACHED(); | |
| 589 } | |
| 590 | |
| 591 aec3.AnalyzeCapture(&capture_buffer); | |
| 592 if (sample_rate_hz > 16000) { | |
| 593 capture_buffer.SplitIntoFrequencyBands(); | |
| 594 render_buffer.SplitIntoFrequencyBands(); | |
| 595 } | |
| 596 | |
| 597 PopulateInputFrame(frame_length, num_bands, frame_index, | |
| 598 &capture_buffer.split_bands_f(0)[0], 100); | |
| 599 PopulateInputFrame(frame_length, num_bands, frame_index, | |
| 600 &render_buffer.split_bands_f(0)[0], 0); | |
| 601 | |
| 602 EXPECT_TRUE(aec3.AnalyzeRender(&render_buffer)); | |
| 603 aec3.ProcessCapture(&capture_buffer, false); | |
| 604 | |
| 605 switch (saturation_variant) { | |
| 606 case SaturationTestVariant::kNone: | |
| 607 EXPECT_EQ(0.f, capture_buffer.split_bands_f(0)[0][0]); | |
| 608 break; | |
| 609 case SaturationTestVariant::kOneNegative: | |
| 610 if (frame_index > 0) { | |
| 611 EXPECT_EQ(1.f, capture_buffer.split_bands_f(0)[0][0]); | |
| 612 } | |
| 613 break; | |
| 614 case SaturationTestVariant::kOnePositive: | |
| 615 if (frame_index > 0) { | |
| 616 EXPECT_EQ(1.f, capture_buffer.split_bands_f(0)[0][0]); | |
| 617 } | |
| 618 break; | |
| 619 case SaturationTestVariant::kNotSticky: | |
| 620 if (frame_index > 15) { | |
| 621 EXPECT_EQ(0.f, capture_buffer.split_bands_f(0)[0][0]); | |
| 622 } | |
| 623 break; | |
| 624 default: | |
|
hlundin-webrtc
2016/12/20 15:10:35
Remove default.
peah-webrtc
2016/12/21 23:13:52
Done.
| |
| 625 RTC_NOTREACHED(); | |
| 626 } | |
| 627 } | |
| 628 } | |
| 629 | |
| 630 void RunRenderPipelineSwapQueueTest(int sample_rate_hz) { | |
|
hlundin-webrtc
2016/12/20 15:10:35
Comment on what this test does.
peah-webrtc
2016/12/21 23:13:51
Done.
| |
| 631 const size_t num_bands = NumBandsForRate(sample_rate_hz); | |
| 632 const size_t frame_length = sample_rate_hz == 8000 ? 80 : 160; | |
| 633 float input_frame_data[num_bands][frame_length]; | |
| 634 float* input_frame[num_bands]; | |
| 635 float output_frame_data[num_bands][frame_length]; | |
| 636 float* output_frame[num_bands]; | |
| 637 for (size_t k = 0; k < num_bands; ++k) { | |
| 638 input_frame[k] = &input_frame_data[k][0]; | |
| 639 output_frame[k] = &output_frame_data[k][0]; | |
| 640 } | |
| 641 | |
| 642 const int samples_per_channel = rtc::CheckedDivExact(sample_rate_hz, 100); | |
| 643 AudioBuffer capture_buffer(samples_per_channel, 1, samples_per_channel, 1, | |
| 644 samples_per_channel); | |
| 645 AudioBuffer render_buffer(samples_per_channel, 1, samples_per_channel, 1, | |
| 646 samples_per_channel); | |
| 647 | |
| 648 EchoCanceller3 aec3(sample_rate_hz, false, | |
| 649 new BlockProcessorRenderRedirect(num_bands)); | |
| 650 | |
| 651 const size_t kNumFramesToProcess = 30; | |
| 652 for (size_t frame_index = 0; frame_index < kNumFramesToProcess; | |
| 653 ++frame_index) { | |
| 654 if (sample_rate_hz > 16000) { | |
| 655 render_buffer.SplitIntoFrequencyBands(); | |
| 656 } | |
| 657 PopulateInputFrame(frame_length, num_bands, frame_index, | |
| 658 &render_buffer.split_bands_f(0)[0], 0); | |
| 659 | |
| 660 EXPECT_TRUE(aec3.AnalyzeRender(&render_buffer)); | |
| 661 } | |
| 662 | |
| 663 for (size_t frame_index = 0; frame_index < kNumFramesToProcess; | |
| 664 ++frame_index) { | |
| 665 aec3.AnalyzeCapture(&capture_buffer); | |
| 666 if (sample_rate_hz > 16000) { | |
| 667 capture_buffer.SplitIntoFrequencyBands(); | |
| 668 } | |
| 669 | |
| 670 PopulateInputFrame(frame_length, num_bands, frame_index, | |
| 671 &capture_buffer.split_bands_f(0)[0], 100); | |
| 672 | |
| 673 aec3.ProcessCapture(&capture_buffer, false); | |
| 674 EXPECT_TRUE(VerifyInputFrame(frame_length, num_bands, frame_index, | |
| 675 &capture_buffer.split_bands_f(0)[0], -64)); | |
| 676 } | |
| 677 } | |
| 678 | |
| 679 void RunRenderPipelineSwapQueueOverrunReturnValueTest(int sample_rate_hz) { | |
|
hlundin-webrtc
2016/12/20 15:10:35
Comment...
peah-webrtc
2016/12/21 23:13:52
Done.
| |
| 680 const size_t num_bands = NumBandsForRate(sample_rate_hz); | |
| 681 const size_t frame_length = sample_rate_hz == 8000 ? 80 : 160; | |
| 682 float input_frame_data[num_bands][frame_length]; | |
| 683 float* input_frame[num_bands]; | |
| 684 float output_frame_data[num_bands][frame_length]; | |
| 685 float* output_frame[num_bands]; | |
| 686 for (size_t k = 0; k < num_bands; ++k) { | |
| 687 input_frame[k] = &input_frame_data[k][0]; | |
| 688 output_frame[k] = &output_frame_data[k][0]; | |
| 689 } | |
| 690 | |
| 691 const int samples_per_channel = rtc::CheckedDivExact(sample_rate_hz, 100); | |
| 692 AudioBuffer capture_buffer(samples_per_channel, 1, samples_per_channel, 1, | |
| 693 samples_per_channel); | |
| 694 AudioBuffer render_buffer(samples_per_channel, 1, samples_per_channel, 1, | |
| 695 samples_per_channel); | |
| 696 | |
| 697 EchoCanceller3 aec3(sample_rate_hz, false); | |
| 698 | |
| 699 const size_t kNumFramesToProcess = 30; | |
| 700 for (size_t k = 0; k < 2; ++k) { | |
| 701 for (size_t frame_index = 0; frame_index < kNumFramesToProcess; | |
| 702 ++frame_index) { | |
| 703 if (sample_rate_hz > 16000) { | |
| 704 render_buffer.SplitIntoFrequencyBands(); | |
| 705 } | |
| 706 PopulateInputFrame(frame_length, num_bands, frame_index, | |
| 707 &render_buffer.split_bands_f(0)[0], 0); | |
| 708 | |
| 709 if (k == 0) { | |
| 710 EXPECT_TRUE(aec3.AnalyzeRender(&render_buffer)); | |
| 711 } else { | |
| 712 EXPECT_FALSE(aec3.AnalyzeRender(&render_buffer)); | |
| 713 } | |
| 714 } | |
| 715 } | |
| 716 } | |
| 717 | |
| 718 void RunBlockerAndFramerTest(int sample_rate_hz) { | |
|
hlundin-webrtc
2016/12/20 15:10:35
This looks like unit tests for FrameBlocker and Bl
peah-webrtc
2016/12/21 23:13:51
Done.
| |
| 719 const size_t num_bands = NumBandsForRate(sample_rate_hz); | |
| 720 const size_t frame_length = sample_rate_hz == 8000 ? 80 : 160; | |
| 721 const size_t num_sub_frames = frame_length / 80; | |
| 722 float input_frame_data[num_bands][frame_length]; | |
| 723 float* input_frame[num_bands]; | |
| 724 float output_frame_data[num_bands][frame_length]; | |
| 725 float* output_frame[num_bands]; | |
| 726 for (size_t k = 0; k < num_bands; ++k) { | |
| 727 input_frame[k] = &input_frame_data[k][0]; | |
| 728 output_frame[k] = &output_frame_data[k][0]; | |
| 729 } | |
| 730 | |
| 731 std::vector<std::vector<float>> block(num_bands, std::vector<float>(64, 0.f)); | |
| 732 FrameBlocker blocker(num_bands); | |
| 733 BlockFramer framer(num_bands); | |
| 734 | |
| 735 for (size_t frame_index = 0; frame_index < 20; ++frame_index) { | |
| 736 PopulateInputFrame(frame_length, num_bands, frame_index, input_frame, 0); | |
| 737 for (size_t sub_frame_index = 0; sub_frame_index < num_sub_frames; | |
| 738 ++sub_frame_index) { | |
| 739 rtc::ArrayView<float> sub_frame_view_data[num_bands]; | |
| 740 rtc::ArrayView<const float> sub_frame_const_view_data[num_bands]; | |
| 741 auto sub_frame_view = rtc::ArrayView<rtc::ArrayView<float>>( | |
| 742 &sub_frame_view_data[0], num_bands); | |
| 743 auto sub_frame_const_view = rtc::ArrayView<rtc::ArrayView<const float>>( | |
| 744 &sub_frame_const_view_data[0], num_bands); | |
| 745 FillSubFrameView(output_frame, sub_frame_index, sub_frame_view); | |
| 746 FillSubFrameView(input_frame, sub_frame_index, sub_frame_const_view); | |
| 747 | |
| 748 blocker.InsertSubFrameAndExtractBlock(sub_frame_const_view, &block); | |
| 749 | |
| 750 framer.InsertBlockAndExtractSubFrame(block, sub_frame_view); | |
| 751 if ((frame_index * num_sub_frames + sub_frame_index + 1) % 4 == 0) { | |
| 752 EXPECT_TRUE(blocker.IsBlockAvailable()); | |
| 753 } else { | |
| 754 EXPECT_FALSE(blocker.IsBlockAvailable()); | |
| 755 } | |
| 756 if (blocker.IsBlockAvailable()) { | |
| 757 blocker.ExtractBlock(&block); | |
| 758 framer.InsertBlock(block); | |
| 759 } | |
| 760 } | |
| 761 EXPECT_TRUE(VerifyInputFrame(frame_length, num_bands, frame_index, | |
| 762 output_frame, -64)); | |
| 763 } | |
| 764 } | |
| 765 | |
| 766 } // namespace | |
| 767 | |
| 768 TEST(EchoCanceller3Pipeline, CaptureBitexactness) { | |
| 769 for (auto rate : {8000, 16000, 32000, 48000}) { | |
|
hlundin-webrtc
2016/12/20 15:10:36
This makes it tricky to know which test case is ac
peah-webrtc
2016/12/21 23:13:52
Good point! I went for SCOPED_TRACE.
PTAL
| |
| 770 RunCapturePipelineVerificationTest(rate); | |
| 771 } | |
| 772 } | |
| 773 | |
| 774 TEST(EchoCanceller3Pipeline, RenderBitexactness) { | |
| 775 for (auto rate : {8000, 16000, 32000, 48000}) { | |
| 776 RunRenderPipelineVerificationTest(rate); | |
| 777 } | |
| 778 } | |
| 779 | |
| 780 TEST(EchoCanceller3Pipeline, CaptureSaturation) { | |
| 781 auto variants = { | |
| 782 SaturationTestVariant::kNone, SaturationTestVariant::kOneNegative, | |
| 783 SaturationTestVariant::kOnePositive, SaturationTestVariant::kNotSticky}; | |
| 784 for (auto rate : {8000, 16000, 32000, 48000}) { | |
| 785 for (auto variant : variants) { | |
| 786 RunCaptureSaturationVerificationTest(rate, variant); | |
| 787 } | |
| 788 } | |
| 789 } | |
| 790 | |
| 791 TEST(EchoCanceller3Pipeline, EchoPathChange) { | |
| 792 auto variants = {EchoPathTestVariant::kNone, EchoPathTestVariant::kOneSticky, | |
| 793 EchoPathTestVariant::kOneNonSticky}; | |
| 794 for (auto rate : {8000, 16000, 32000, 48000}) { | |
| 795 for (auto variant : variants) { | |
| 796 RunEchoPathChangeVerificationTest(rate, variant); | |
| 797 } | |
| 798 } | |
| 799 } | |
| 800 | |
| 801 TEST(EchoCanceller3Pipeline, EchoLeakage) { | |
| 802 auto variants = {EchoLeakageTestVariant::kNone, | |
| 803 EchoLeakageTestVariant::kFalseSticky, | |
| 804 EchoLeakageTestVariant::kTrueSticky, | |
| 805 EchoLeakageTestVariant::kTrueNonSticky}; | |
| 806 for (auto rate : {8000, 16000, 32000, 48000}) { | |
| 807 for (auto variant : variants) { | |
| 808 RunEchoLeakageVerificationTest(rate, variant); | |
| 809 } | |
| 810 } | |
| 811 } | |
| 812 | |
| 813 TEST(EchoCanceller3Pipeline, RenderSwapQueue) { | |
| 814 for (auto rate : {8000, 16000, 32000, 48000}) { | |
| 815 RunRenderPipelineSwapQueueTest(rate); | |
| 816 } | |
| 817 } | |
| 818 | |
| 819 TEST(EchoCanceller3Pipeline, RenderSwapQueueOverrunReturnValue) { | |
| 820 for (auto rate : {8000, 16000, 32000, 48000}) { | |
| 821 RunRenderPipelineSwapQueueOverrunReturnValueTest(rate); | |
| 822 } | |
| 823 } | |
| 824 | |
| 825 TEST(EchoCanceller3Pipeline, BlockerAndFramer) { | |
| 826 for (auto rate : {8000, 16000, 32000, 48000}) { | |
| 827 RunBlockerAndFramerTest(rate); | |
| 828 } | |
| 829 } | |
| 830 | |
| 831 } // namespace webrtc | |
| OLD | NEW |