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 #include <vector> | |
|
hlundin-webrtc
2016/06/27 11:21:15
Nit: I tend to like a blank line after the copyrig
peah-webrtc
2016/06/27 22:51:49
Done.
| |
| 11 | |
| 12 #include "testing/gtest/include/gtest/gtest.h" | |
| 13 #include "webrtc/base/array_view.h" | |
| 14 #include "webrtc/base/random.h" | |
| 15 #include "webrtc/modules/audio_processing/audio_buffer.h" | |
| 16 #include "webrtc/modules/audio_processing/include/audio_processing.h" | |
| 17 #include "webrtc/modules/audio_processing/level_controller/level_controller.h" | |
| 18 #include "webrtc/modules/audio_processing/test/audio_buffer_tools.h" | |
| 19 #include "webrtc/modules/audio_processing/test/bitexactness_tools.h" | |
| 20 #include "webrtc/system_wrappers/include/clock.h" | |
| 21 #include "webrtc/test/testsupport/perf_test.h" | |
| 22 | |
| 23 namespace webrtc { | |
| 24 namespace { | |
| 25 | |
| 26 const size_t kNumFramesToProcess = 100; | |
| 27 | |
| 28 struct SimulatorBuffers { | |
| 29 SimulatorBuffers(int render_input_sample_rate_hz, | |
| 30 int capture_input_sample_rate_hz, | |
| 31 int render_output_sample_rate_hz, | |
| 32 int capture_output_sample_rate_hz, | |
| 33 size_t num_render_input_channels, | |
| 34 size_t num_capture_input_channels, | |
| 35 size_t num_render_output_channels, | |
| 36 size_t num_capture_output_channels) { | |
| 37 Random rand_gen(42); | |
| 38 CreateConfigAndBuffer(render_input_sample_rate_hz, | |
| 39 num_render_input_channels, &rand_gen, | |
| 40 &render_input_buffer, &render_input_config, | |
| 41 &render_input, &render_input_samples); | |
| 42 | |
| 43 CreateConfigAndBuffer(render_output_sample_rate_hz, | |
| 44 num_render_output_channels, &rand_gen, | |
| 45 &render_output_buffer, &render_output_config, | |
| 46 &render_output, &render_output_samples); | |
| 47 | |
| 48 CreateConfigAndBuffer(capture_input_sample_rate_hz, | |
| 49 num_capture_input_channels, &rand_gen, | |
| 50 &capture_input_buffer, &capture_input_config, | |
| 51 &capture_input, &capture_input_samples); | |
| 52 | |
| 53 CreateConfigAndBuffer(capture_output_sample_rate_hz, | |
| 54 num_capture_output_channels, &rand_gen, | |
| 55 &capture_output_buffer, &capture_output_config, | |
| 56 &capture_output, &capture_output_samples); | |
| 57 | |
| 58 UpdateInputBuffers(); | |
| 59 } | |
| 60 | |
| 61 void CreateConfigAndBuffer(int sample_rate_hz, | |
| 62 size_t num_channels, | |
| 63 Random* rand_gen, | |
| 64 std::unique_ptr<AudioBuffer>* buffer, | |
| 65 StreamConfig* config, | |
| 66 std::vector<float*>* buffer_data, | |
| 67 std::vector<float>* buffer_data_samples) { | |
| 68 int samples_per_channel = rtc::CheckedDivExact(sample_rate_hz, 100); | |
| 69 *config = StreamConfig(sample_rate_hz, num_channels, false); | |
| 70 buffer->reset(new AudioBuffer(config->num_frames(), config->num_channels(), | |
| 71 config->num_frames(), config->num_channels(), | |
| 72 config->num_frames())); | |
| 73 | |
| 74 buffer_data_samples->resize(samples_per_channel * num_channels); | |
| 75 for (auto& v : *buffer_data_samples) { | |
| 76 v = rand_gen->Rand<float>(); | |
| 77 } | |
| 78 | |
| 79 buffer_data->resize(num_channels); | |
| 80 for (size_t ch = 0; ch < num_channels; ++ch) { | |
| 81 (*buffer_data)[ch] = &(*buffer_data_samples)[ch * samples_per_channel]; | |
| 82 } | |
| 83 } | |
| 84 | |
| 85 void UpdateInputBuffers() { | |
| 86 test::CopyVectorToAudioBuffer(capture_input_config, capture_input_samples, | |
| 87 capture_input_buffer.get()); | |
| 88 test::CopyVectorToAudioBuffer(render_input_config, render_input_samples, | |
| 89 render_input_buffer.get()); | |
| 90 } | |
| 91 | |
| 92 std::unique_ptr<AudioBuffer> render_input_buffer; | |
| 93 std::unique_ptr<AudioBuffer> capture_input_buffer; | |
| 94 std::unique_ptr<AudioBuffer> render_output_buffer; | |
| 95 std::unique_ptr<AudioBuffer> capture_output_buffer; | |
| 96 StreamConfig render_input_config; | |
| 97 StreamConfig capture_input_config; | |
| 98 StreamConfig render_output_config; | |
| 99 StreamConfig capture_output_config; | |
| 100 std::vector<float*> render_input; | |
| 101 std::vector<float> render_input_samples; | |
| 102 std::vector<float*> capture_input; | |
| 103 std::vector<float> capture_input_samples; | |
| 104 std::vector<float*> render_output; | |
| 105 std::vector<float> render_output_samples; | |
| 106 std::vector<float*> capture_output; | |
| 107 std::vector<float> capture_output_samples; | |
| 108 }; | |
| 109 | |
| 110 class SubmodulePerformanceTimer { | |
| 111 public: | |
| 112 explicit SubmodulePerformanceTimer(size_t num_values_to_store) | |
|
hlundin-webrtc
2016/06/27 11:21:15
Why have an explicit limitation on the size? Vecto
peah-webrtc
2016/06/27 22:51:48
Good point! But since some of the timers are neste
| |
| 113 : clock_(webrtc::Clock::GetRealTimeClock()) { | |
| 114 timestamps_.resize(num_values_to_store); | |
|
hlundin-webrtc
2016/06/27 11:21:15
You can do this in the initializer list as timesta
peah-webrtc
2016/06/27 22:51:48
I removed the fixed size limitation so this is no
| |
| 115 } | |
| 116 | |
| 117 void ResetTimer() { start_timestamp_ = clock_->TimeInMicroseconds(); } | |
|
hlundin-webrtc
2016/06/27 11:21:15
Make start_timestamp_ an rtc::Optional. Initialize
peah-webrtc
2016/06/27 22:51:48
Great suggestion!
Done.
| |
| 118 void AddTimeStamp() { | |
| 119 RTC_CHECK_LE(num_timestamps_stored_, timestamps_.size()); | |
| 120 timestamps_[num_timestamps_stored_] = | |
|
hlundin-webrtc
2016/06/27 11:21:15
You don't need num_timestamps_stored_. Use vector:
peah-webrtc
2016/06/27 22:51:49
Done.
| |
| 121 clock_->TimeInMicroseconds() - start_timestamp_; | |
| 122 ++num_timestamps_stored_; | |
| 123 } | |
| 124 | |
| 125 double GetDurationAverage() const { | |
| 126 RTC_DCHECK_EQ(num_timestamps_stored_, timestamps_.size()); | |
| 127 int64_t durations_sum = 0; | |
|
hlundin-webrtc
2016/06/27 11:21:16
One-liner method:
return timestamps_.empty() ? 0.0
peah-webrtc
2016/06/27 22:51:49
Great suggestion! I chose the latter variant.
Don
| |
| 128 for (auto timestamp : timestamps_) { | |
| 129 durations_sum += timestamp; | |
| 130 } | |
| 131 | |
| 132 RTC_DCHECK_LT(0u, timestamps_.size()); | |
| 133 return static_cast<double>(durations_sum) / timestamps_.size(); | |
| 134 } | |
| 135 | |
| 136 double GetDurationStandardDeviationGetVarianceTime() const { | |
|
hlundin-webrtc
2016/06/27 11:21:16
This is an awkward method name. What does it mean?
peah-webrtc
2016/06/27 22:51:48
Agree! No idea! Changed the name.
Done.
| |
| 137 int32_t average_duration = GetDurationAverage(); | |
|
hlundin-webrtc
2016/06/27 11:21:15
Why int32_t? GetDurationAverage() returns a double
peah-webrtc
2016/06/27 22:51:49
I changed it to double.
Done.
| |
| 138 int64_t variance = 0; | |
|
hlundin-webrtc
2016/06/27 11:21:16
You can make this a one-liner too, with a lambda,
peah-webrtc
2016/06/27 22:51:48
Not sure either. But it looks great so I added it!
| |
| 139 for (auto timestamp : timestamps_) { | |
| 140 variance += timestamp - average_duration; | |
| 141 } | |
| 142 | |
| 143 RTC_DCHECK_LT(0u, timestamps_.size()); | |
| 144 return sqrt(static_cast<double>(variance) / timestamps_.size()); | |
| 145 } | |
| 146 | |
| 147 private: | |
| 148 webrtc::Clock* clock_; | |
| 149 int64_t start_timestamp_ = 0; | |
| 150 size_t num_timestamps_stored_ = 0; | |
| 151 std::vector<int64_t> timestamps_; | |
|
hlundin-webrtc
2016/06/27 11:21:15
timestamps_us_
peah-webrtc
2016/06/27 22:51:49
Done.
| |
| 152 }; | |
| 153 | |
| 154 std::string FormPerformanceMeasureString( | |
| 155 const SubmodulePerformanceTimer& timer) { | |
| 156 double average = timer.GetDurationAverage(); | |
|
hlundin-webrtc
2016/06/27 11:21:15
You don't need these local variables. Just plug th
peah-webrtc
2016/06/27 22:51:49
Done.
| |
| 157 double standard_dev = timer.GetDurationStandardDeviationGetVarianceTime(); | |
| 158 std::string s = std::to_string(average); | |
| 159 s += ", "; | |
| 160 s += std::to_string(standard_dev); | |
| 161 return s; | |
| 162 } | |
| 163 | |
| 164 void RunStandatoleSubmodule(int sample_rate_hz, size_t num_channels) { | |
|
hlundin-webrtc
2016/06/27 11:21:16
Standatole?
peah-webrtc
2016/06/27 22:51:49
Absolutely!
Done.
| |
| 165 SimulatorBuffers buffers(sample_rate_hz, sample_rate_hz, sample_rate_hz, | |
| 166 sample_rate_hz, num_channels, num_channels, | |
| 167 num_channels, num_channels); | |
| 168 SubmodulePerformanceTimer timer(kNumFramesToProcess); | |
| 169 | |
| 170 LevelController level_controller; | |
| 171 level_controller.Initialize(sample_rate_hz, num_channels); | |
| 172 | |
| 173 for (size_t frame_no = 0; frame_no < kNumFramesToProcess; ++frame_no) { | |
| 174 buffers.UpdateInputBuffers(); | |
| 175 | |
| 176 timer.ResetTimer(); | |
|
hlundin-webrtc
2016/06/27 11:21:15
Based on the use case, I suggest you rename the ti
peah-webrtc
2016/06/27 22:51:48
Makes sense!
Done.
| |
| 177 level_controller.Process(buffers.capture_input_buffer.get()); | |
| 178 timer.AddTimeStamp(); | |
| 179 } | |
| 180 webrtc::test::PrintResultMeanAndError( | |
| 181 "level_controller_call_durations", | |
| 182 "_" + std::to_string(sample_rate_hz) + "Hz_" + | |
| 183 std::to_string(num_channels) + "_channels", | |
| 184 "StandaloneLevelControl", FormPerformanceMeasureString(timer), "us", | |
| 185 false); | |
| 186 } | |
| 187 | |
| 188 void RunTogetherWithApm(std::string test_description, | |
| 189 int render_input_sample_rate_hz, | |
| 190 int render_output_sample_rate_hz, | |
| 191 int capture_input_sample_rate_hz, | |
| 192 int capture_output_sample_rate_hz, | |
| 193 size_t num_channels, | |
| 194 bool use_mobile_aec, | |
| 195 bool include_default_apm_processing) { | |
| 196 SimulatorBuffers buffers( | |
| 197 render_input_sample_rate_hz, capture_input_sample_rate_hz, | |
| 198 render_output_sample_rate_hz, capture_output_sample_rate_hz, num_channels, | |
| 199 num_channels, num_channels, num_channels); | |
| 200 SubmodulePerformanceTimer render_timer(kNumFramesToProcess); | |
| 201 SubmodulePerformanceTimer capture_timer(kNumFramesToProcess); | |
| 202 SubmodulePerformanceTimer total_timer(kNumFramesToProcess); | |
| 203 | |
| 204 Config config; | |
| 205 if (include_default_apm_processing) { | |
| 206 config.Set<DelayAgnostic>(new DelayAgnostic(true)); | |
| 207 config.Set<ExtendedFilter>(new ExtendedFilter(true)); | |
| 208 } | |
| 209 config.Set<LevelControl>(new LevelControl(true)); | |
| 210 | |
| 211 std::unique_ptr<AudioProcessing> apm; | |
| 212 apm.reset(AudioProcessing::Create(config)); | |
|
hlundin-webrtc
2016/06/27 11:21:16
Can Create fail? If so, ASSERT_TRUE that apm is ok
peah-webrtc
2016/06/27 22:51:49
Good point! It can fail, so I'll add that.
Done.
| |
| 213 | |
| 214 apm->gain_control()->Enable(include_default_apm_processing); | |
|
hlundin-webrtc
2016/06/27 11:21:15
All the Enable methods seem to have a return value
peah-webrtc
2016/06/27 22:51:48
Done.
| |
| 215 if (use_mobile_aec) { | |
| 216 apm->echo_cancellation()->Enable(false); | |
| 217 apm->echo_control_mobile()->Enable(include_default_apm_processing); | |
| 218 } else { | |
| 219 apm->echo_cancellation()->Enable(include_default_apm_processing); | |
| 220 apm->echo_control_mobile()->Enable(false); | |
| 221 } | |
| 222 apm->high_pass_filter()->Enable(include_default_apm_processing); | |
| 223 apm->noise_suppression()->Enable(include_default_apm_processing); | |
| 224 apm->voice_detection()->Enable(include_default_apm_processing); | |
| 225 apm->level_estimator()->Enable(include_default_apm_processing); | |
| 226 | |
| 227 StreamConfig render_input_config(render_input_sample_rate_hz, num_channels, | |
| 228 false); | |
| 229 StreamConfig render_output_config(render_output_sample_rate_hz, num_channels, | |
| 230 false); | |
| 231 StreamConfig capture_input_config(capture_input_sample_rate_hz, num_channels, | |
| 232 false); | |
| 233 StreamConfig capture_output_config(capture_output_sample_rate_hz, | |
| 234 num_channels, false); | |
| 235 | |
| 236 for (size_t frame_no = 0; frame_no < kNumFramesToProcess; ++frame_no) { | |
| 237 buffers.UpdateInputBuffers(); | |
| 238 | |
| 239 total_timer.ResetTimer(); | |
| 240 render_timer.ResetTimer(); | |
| 241 int error = apm->ProcessReverseStream( | |
|
hlundin-webrtc
2016/06/27 11:21:15
Don't store the return value.
ASSERT_EQ(AudioProce
peah-webrtc
2016/06/27 22:51:48
Done.
| |
| 242 &buffers.render_input[0], render_input_config, render_output_config, | |
| 243 &buffers.render_output[0]); | |
| 244 | |
| 245 ASSERT_EQ(AudioProcessing::kNoError, error); | |
| 246 | |
| 247 render_timer.AddTimeStamp(); | |
| 248 | |
| 249 capture_timer.ResetTimer(); | |
| 250 apm->set_stream_delay_ms(0); | |
|
hlundin-webrtc
2016/06/27 11:21:15
Can it fail? ASSERT_EQ?
peah-webrtc
2016/06/27 22:51:48
Done.
| |
| 251 error = | |
| 252 apm->ProcessStream(&buffers.capture_input[0], capture_input_config, | |
| 253 capture_output_config, &buffers.capture_output[0]); | |
| 254 | |
| 255 capture_timer.AddTimeStamp(); | |
| 256 total_timer.AddTimeStamp(); | |
| 257 ASSERT_EQ(AudioProcessing::kNoError, error); | |
| 258 } | |
| 259 | |
| 260 webrtc::test::PrintResultMeanAndError( | |
| 261 "level_controller_call_durations", | |
| 262 "_" + std::to_string(render_input_sample_rate_hz) + "_" + | |
| 263 std::to_string(render_output_sample_rate_hz) + "_" + | |
| 264 std::to_string(capture_input_sample_rate_hz) + "_" + | |
| 265 std::to_string(capture_output_sample_rate_hz) + "Hz_" + | |
| 266 std::to_string(num_channels) + "_channels" + "_render", | |
| 267 test_description, FormPerformanceMeasureString(render_timer), "us", | |
| 268 false); | |
| 269 webrtc::test::PrintResultMeanAndError( | |
| 270 "level_controller_call_durations", | |
| 271 "_" + std::to_string(render_input_sample_rate_hz) + "_" + | |
| 272 std::to_string(render_output_sample_rate_hz) + "_" + | |
| 273 std::to_string(capture_input_sample_rate_hz) + "_" + | |
| 274 std::to_string(capture_output_sample_rate_hz) + "Hz_" + | |
| 275 std::to_string(num_channels) + "_channels" + "_capture", | |
| 276 test_description, FormPerformanceMeasureString(capture_timer), "us", | |
| 277 false); | |
| 278 webrtc::test::PrintResultMeanAndError( | |
| 279 "level_controller_call_durations", | |
| 280 "_" + std::to_string(render_input_sample_rate_hz) + "_" + | |
| 281 std::to_string(render_output_sample_rate_hz) + "_" + | |
| 282 std::to_string(capture_input_sample_rate_hz) + "_" + | |
| 283 std::to_string(capture_output_sample_rate_hz) + "Hz_" + | |
| 284 std::to_string(num_channels) + "_channels" + "_total", | |
| 285 test_description, FormPerformanceMeasureString(total_timer), "us", false); | |
| 286 } | |
| 287 | |
| 288 } // namespace | |
| 289 | |
| 290 TEST(LevelControllerPerformanceTest, StandaloneProcessing) { | |
| 291 int sample_rates_to_test[] = { | |
| 292 AudioProcessing::kSampleRate8kHz, AudioProcessing::kSampleRate16kHz, | |
| 293 AudioProcessing::kSampleRate32kHz, AudioProcessing::kSampleRate48kHz}; | |
|
hlundin-webrtc
2016/06/27 11:21:15
No 44100 Hz here?
peah-webrtc
2016/06/27 22:51:49
No. The submodule only supports native rates (8, 1
hlundin-webrtc
2016/06/28 11:29:00
Acknowledged.
peah-webrtc
2016/06/28 22:19:37
Acknowledged.
| |
| 294 for (auto sample_rate : sample_rates_to_test) { | |
| 295 for (size_t num_channels = 1; num_channels <= 2; ++num_channels) { | |
| 296 RunStandatoleSubmodule(sample_rate, num_channels); | |
| 297 } | |
| 298 } | |
| 299 } | |
| 300 | |
| 301 TEST(LevelControllerPerformanceTest, ProcessingViaApm) { | |
| 302 int sample_rates_to_test[] = {AudioProcessing::kSampleRate8kHz, | |
| 303 AudioProcessing::kSampleRate16kHz, | |
| 304 AudioProcessing::kSampleRate32kHz, | |
| 305 AudioProcessing::kSampleRate48kHz, 44100}; | |
| 306 for (auto capture_input_sample_rate_hz : sample_rates_to_test) { | |
| 307 for (auto capture_output_sample_rate_hz : sample_rates_to_test) { | |
| 308 for (size_t num_channels = 1; num_channels <= 2; ++num_channels) { | |
| 309 RunTogetherWithApm("SimpleLevelControlViaApm", 48000, 48000, | |
| 310 capture_input_sample_rate_hz, | |
| 311 capture_output_sample_rate_hz, num_channels, false, | |
| 312 false); | |
| 313 } | |
| 314 } | |
| 315 } | |
| 316 } | |
| 317 | |
| 318 TEST(LevelControllerPerformanceTest, InteractionWithDefaultApm) { | |
| 319 int sample_rates_to_test[] = {AudioProcessing::kSampleRate8kHz, | |
| 320 AudioProcessing::kSampleRate16kHz, | |
| 321 AudioProcessing::kSampleRate32kHz, | |
| 322 AudioProcessing::kSampleRate48kHz, 44100}; | |
| 323 for (auto capture_input_sample_rate_hz : sample_rates_to_test) { | |
| 324 for (auto capture_output_sample_rate_hz : sample_rates_to_test) { | |
| 325 for (size_t num_channels = 1; num_channels <= 2; ++num_channels) { | |
| 326 RunTogetherWithApm("LevelControlAndDefaultDesktopApm", 48000, 48000, | |
| 327 capture_input_sample_rate_hz, | |
| 328 capture_output_sample_rate_hz, num_channels, false, | |
| 329 true); | |
| 330 RunTogetherWithApm("LevelControlAndDefaultMobileApm", 48000, 48000, | |
| 331 capture_input_sample_rate_hz, | |
| 332 capture_output_sample_rate_hz, num_channels, true, | |
| 333 true); | |
| 334 } | |
| 335 } | |
| 336 } | |
| 337 } | |
| 338 | |
| 339 } // namespace webrtc | |
| OLD | NEW |