OLD | NEW |
1 /* | 1 /* |
2 * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. | 2 * Copyright (c) 2015 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 #include <stddef.h> // size_t | 11 #include <stddef.h> // size_t |
12 | 12 |
13 #include <memory> | |
14 #include <string> | 13 #include <string> |
15 #include <vector> | 14 #include <vector> |
16 | 15 |
17 #include "testing/gtest/include/gtest/gtest.h" | 16 #include "testing/gtest/include/gtest/gtest.h" |
18 #include "webrtc/base/checks.h" | |
19 #include "webrtc/common_audio/channel_buffer.h" | |
20 #include "webrtc/modules/audio_coding/neteq/tools/resample_input_audio_file.h" | 17 #include "webrtc/modules/audio_coding/neteq/tools/resample_input_audio_file.h" |
21 #include "webrtc/modules/audio_processing/debug.pb.h" | 18 #include "webrtc/modules/audio_processing/test/debug_dump_replayer.h" |
22 #include "webrtc/modules/audio_processing/include/audio_processing.h" | |
23 #include "webrtc/modules/audio_processing/test/protobuf_utils.h" | |
24 #include "webrtc/modules/audio_processing/test/test_utils.h" | 19 #include "webrtc/modules/audio_processing/test/test_utils.h" |
25 #include "webrtc/test/testsupport/fileutils.h" | 20 #include "webrtc/test/testsupport/fileutils.h" |
26 | 21 |
| 22 |
27 namespace webrtc { | 23 namespace webrtc { |
28 namespace test { | 24 namespace test { |
29 | 25 |
30 namespace { | 26 namespace { |
31 | 27 |
32 void MaybeResetBuffer(std::unique_ptr<ChannelBuffer<float>>* buffer, | 28 void MaybeResetBuffer(std::unique_ptr<ChannelBuffer<float>>* buffer, |
33 const StreamConfig& config) { | 29 const StreamConfig& config) { |
34 auto& buffer_ref = *buffer; | 30 auto& buffer_ref = *buffer; |
35 if (!buffer_ref.get() || buffer_ref->num_frames() != config.num_frames() || | 31 if (!buffer_ref.get() || buffer_ref->num_frames() != config.num_frames() || |
36 buffer_ref->num_channels() != config.num_channels()) { | 32 buffer_ref->num_channels() != config.num_channels()) { |
(...skipping 189 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
226 for (size_t i = 0; i < num_frames; ++i) { | 222 for (size_t i = 0; i < num_frames; ++i) { |
227 buffer[channel][i] = S16ToFloat(signal[i * channels + channel]); | 223 buffer[channel][i] = S16ToFloat(signal[i * channels + channel]); |
228 } | 224 } |
229 } | 225 } |
230 } | 226 } |
231 | 227 |
232 } // namespace | 228 } // namespace |
233 | 229 |
234 class DebugDumpTest : public ::testing::Test { | 230 class DebugDumpTest : public ::testing::Test { |
235 public: | 231 public: |
236 DebugDumpTest(); | |
237 | |
238 // VerifyDebugDump replays a debug dump using APM and verifies that the result | 232 // VerifyDebugDump replays a debug dump using APM and verifies that the result |
239 // is bit-exact-identical to the output channel in the dump. This is only | 233 // is bit-exact-identical to the output channel in the dump. This is only |
240 // guaranteed if the debug dump is started on the first frame. | 234 // guaranteed if the debug dump is started on the first frame. |
241 void VerifyDebugDump(const std::string& dump_file_name); | 235 void VerifyDebugDump(const std::string& dump_file_name); |
242 | 236 |
243 private: | 237 private: |
244 // Following functions are facilities for replaying debug dumps. | 238 DebugDumpReplayer debug_dump_replayer_; |
245 void OnInitEvent(const audioproc::Init& msg); | |
246 void OnStreamEvent(const audioproc::Stream& msg); | |
247 void OnReverseStreamEvent(const audioproc::ReverseStream& msg); | |
248 void OnConfigEvent(const audioproc::Config& msg); | |
249 | |
250 void MaybeRecreateApm(const audioproc::Config& msg); | |
251 void ConfigureApm(const audioproc::Config& msg); | |
252 | |
253 // Buffer for APM input/output. | |
254 std::unique_ptr<ChannelBuffer<float>> input_; | |
255 std::unique_ptr<ChannelBuffer<float>> reverse_; | |
256 std::unique_ptr<ChannelBuffer<float>> output_; | |
257 | |
258 std::unique_ptr<AudioProcessing> apm_; | |
259 | |
260 StreamConfig input_config_; | |
261 StreamConfig reverse_config_; | |
262 StreamConfig output_config_; | |
263 }; | 239 }; |
264 | 240 |
265 DebugDumpTest::DebugDumpTest() | 241 void DebugDumpTest::VerifyDebugDump(const std::string& in_filename) { |
266 : input_(nullptr), // will be created upon usage. | 242 ASSERT_TRUE(debug_dump_replayer_.SetDumpFile(in_filename)); |
267 reverse_(nullptr), | |
268 output_(nullptr), | |
269 apm_(nullptr) { | |
270 } | |
271 | 243 |
272 void DebugDumpTest::VerifyDebugDump(const std::string& in_filename) { | 244 if (const rtc::Optional<audioproc::Event> event = |
273 FILE* in_file = fopen(in_filename.c_str(), "rb"); | 245 debug_dump_replayer_.GetNextEvent()) { |
274 ASSERT_TRUE(in_file); | 246 debug_dump_replayer_.RunNextEvent(); |
275 audioproc::Event event_msg; | 247 if (event->type() == audioproc::Event::STREAM) { |
276 | 248 const audioproc::Stream* msg = &event->stream(); |
277 while (ReadMessageFromFile(in_file, &event_msg)) { | 249 const StreamConfig output_config = debug_dump_replayer_.GetOutputConfig(); |
278 switch (event_msg.type()) { | 250 const ChannelBuffer<float>* output = debug_dump_replayer_.GetOutput(); |
279 case audioproc::Event::INIT: | 251 // Check that output of APM is bit-exact to the output in the dump. |
280 OnInitEvent(event_msg.init()); | 252 ASSERT_EQ(output_config.num_channels(), |
281 break; | 253 static_cast<size_t>(msg->output_channel_size())); |
282 case audioproc::Event::STREAM: | 254 ASSERT_EQ(output_config.num_frames() * sizeof(float), |
283 OnStreamEvent(event_msg.stream()); | 255 msg->output_channel(0).size()); |
284 break; | 256 for (int i = 0; i < msg->output_channel_size(); ++i) { |
285 case audioproc::Event::REVERSE_STREAM: | 257 ASSERT_EQ(0, memcmp(output->channels()[i], |
286 OnReverseStreamEvent(event_msg.reverse_stream()); | 258 msg->output_channel(i).data(), |
287 break; | 259 msg->output_channel(i).size())); |
288 case audioproc::Event::CONFIG: | 260 } |
289 OnConfigEvent(event_msg.config()); | |
290 break; | |
291 case audioproc::Event::UNKNOWN_EVENT: | |
292 // We do not expect receive UNKNOWN event currently. | |
293 FAIL(); | |
294 } | 261 } |
295 } | 262 } |
296 fclose(in_file); | |
297 } | |
298 | |
299 // OnInitEvent reset the input/output/reserve channel format. | |
300 void DebugDumpTest::OnInitEvent(const audioproc::Init& msg) { | |
301 ASSERT_TRUE(msg.has_num_input_channels()); | |
302 ASSERT_TRUE(msg.has_output_sample_rate()); | |
303 ASSERT_TRUE(msg.has_num_output_channels()); | |
304 ASSERT_TRUE(msg.has_reverse_sample_rate()); | |
305 ASSERT_TRUE(msg.has_num_reverse_channels()); | |
306 | |
307 input_config_ = StreamConfig(msg.sample_rate(), msg.num_input_channels()); | |
308 output_config_ = | |
309 StreamConfig(msg.output_sample_rate(), msg.num_output_channels()); | |
310 reverse_config_ = | |
311 StreamConfig(msg.reverse_sample_rate(), msg.num_reverse_channels()); | |
312 | |
313 MaybeResetBuffer(&input_, input_config_); | |
314 MaybeResetBuffer(&output_, output_config_); | |
315 MaybeResetBuffer(&reverse_, reverse_config_); | |
316 } | |
317 | |
318 // OnStreamEvent replays an input signal and verifies the output. | |
319 void DebugDumpTest::OnStreamEvent(const audioproc::Stream& msg) { | |
320 // APM should have been created. | |
321 ASSERT_TRUE(apm_.get()); | |
322 | |
323 EXPECT_NOERR(apm_->gain_control()->set_stream_analog_level(msg.level())); | |
324 EXPECT_NOERR(apm_->set_stream_delay_ms(msg.delay())); | |
325 apm_->echo_cancellation()->set_stream_drift_samples(msg.drift()); | |
326 if (msg.has_keypress()) | |
327 apm_->set_stream_key_pressed(msg.keypress()); | |
328 else | |
329 apm_->set_stream_key_pressed(true); | |
330 | |
331 ASSERT_EQ(input_config_.num_channels(), | |
332 static_cast<size_t>(msg.input_channel_size())); | |
333 ASSERT_EQ(input_config_.num_frames() * sizeof(float), | |
334 msg.input_channel(0).size()); | |
335 | |
336 for (int i = 0; i < msg.input_channel_size(); ++i) { | |
337 memcpy(input_->channels()[i], msg.input_channel(i).data(), | |
338 msg.input_channel(i).size()); | |
339 } | |
340 | |
341 ASSERT_EQ(AudioProcessing::kNoError, | |
342 apm_->ProcessStream(input_->channels(), input_config_, | |
343 output_config_, output_->channels())); | |
344 | |
345 // Check that output of APM is bit-exact to the output in the dump. | |
346 ASSERT_EQ(output_config_.num_channels(), | |
347 static_cast<size_t>(msg.output_channel_size())); | |
348 ASSERT_EQ(output_config_.num_frames() * sizeof(float), | |
349 msg.output_channel(0).size()); | |
350 for (int i = 0; i < msg.output_channel_size(); ++i) { | |
351 ASSERT_EQ(0, memcmp(output_->channels()[i], msg.output_channel(i).data(), | |
352 msg.output_channel(i).size())); | |
353 } | |
354 } | 263 } |
355 | 264 |
356 void DebugDumpTest::OnReverseStreamEvent(const audioproc::ReverseStream& msg) { | |
357 // APM should have been created. | |
358 ASSERT_TRUE(apm_.get()); | |
359 | |
360 ASSERT_GT(msg.channel_size(), 0); | |
361 ASSERT_EQ(reverse_config_.num_channels(), | |
362 static_cast<size_t>(msg.channel_size())); | |
363 ASSERT_EQ(reverse_config_.num_frames() * sizeof(float), | |
364 msg.channel(0).size()); | |
365 | |
366 for (int i = 0; i < msg.channel_size(); ++i) { | |
367 memcpy(reverse_->channels()[i], msg.channel(i).data(), | |
368 msg.channel(i).size()); | |
369 } | |
370 | |
371 ASSERT_EQ(AudioProcessing::kNoError, | |
372 apm_->ProcessReverseStream(reverse_->channels(), | |
373 reverse_config_, | |
374 reverse_config_, | |
375 reverse_->channels())); | |
376 } | |
377 | |
378 void DebugDumpTest::OnConfigEvent(const audioproc::Config& msg) { | |
379 MaybeRecreateApm(msg); | |
380 ConfigureApm(msg); | |
381 } | |
382 | |
383 void DebugDumpTest::MaybeRecreateApm(const audioproc::Config& msg) { | |
384 // These configurations cannot be changed on the fly. | |
385 Config config; | |
386 ASSERT_TRUE(msg.has_aec_delay_agnostic_enabled()); | |
387 config.Set<DelayAgnostic>( | |
388 new DelayAgnostic(msg.aec_delay_agnostic_enabled())); | |
389 | |
390 ASSERT_TRUE(msg.has_noise_robust_agc_enabled()); | |
391 config.Set<ExperimentalAgc>( | |
392 new ExperimentalAgc(msg.noise_robust_agc_enabled())); | |
393 | |
394 ASSERT_TRUE(msg.has_transient_suppression_enabled()); | |
395 config.Set<ExperimentalNs>( | |
396 new ExperimentalNs(msg.transient_suppression_enabled())); | |
397 | |
398 ASSERT_TRUE(msg.has_aec_extended_filter_enabled()); | |
399 config.Set<ExtendedFilter>(new ExtendedFilter( | |
400 msg.aec_extended_filter_enabled())); | |
401 | |
402 // We only create APM once, since changes on these fields should not | |
403 // happen in current implementation. | |
404 if (!apm_.get()) { | |
405 apm_.reset(AudioProcessing::Create(config)); | |
406 } | |
407 } | |
408 | |
409 void DebugDumpTest::ConfigureApm(const audioproc::Config& msg) { | |
410 // AEC configs. | |
411 ASSERT_TRUE(msg.has_aec_enabled()); | |
412 EXPECT_EQ(AudioProcessing::kNoError, | |
413 apm_->echo_cancellation()->Enable(msg.aec_enabled())); | |
414 | |
415 ASSERT_TRUE(msg.has_aec_drift_compensation_enabled()); | |
416 EXPECT_EQ(AudioProcessing::kNoError, | |
417 apm_->echo_cancellation()->enable_drift_compensation( | |
418 msg.aec_drift_compensation_enabled())); | |
419 | |
420 ASSERT_TRUE(msg.has_aec_suppression_level()); | |
421 EXPECT_EQ(AudioProcessing::kNoError, | |
422 apm_->echo_cancellation()->set_suppression_level( | |
423 static_cast<EchoCancellation::SuppressionLevel>( | |
424 msg.aec_suppression_level()))); | |
425 | |
426 // AECM configs. | |
427 ASSERT_TRUE(msg.has_aecm_enabled()); | |
428 EXPECT_EQ(AudioProcessing::kNoError, | |
429 apm_->echo_control_mobile()->Enable(msg.aecm_enabled())); | |
430 | |
431 ASSERT_TRUE(msg.has_aecm_comfort_noise_enabled()); | |
432 EXPECT_EQ(AudioProcessing::kNoError, | |
433 apm_->echo_control_mobile()->enable_comfort_noise( | |
434 msg.aecm_comfort_noise_enabled())); | |
435 | |
436 ASSERT_TRUE(msg.has_aecm_routing_mode()); | |
437 EXPECT_EQ(AudioProcessing::kNoError, | |
438 apm_->echo_control_mobile()->set_routing_mode( | |
439 static_cast<EchoControlMobile::RoutingMode>( | |
440 msg.aecm_routing_mode()))); | |
441 | |
442 // AGC configs. | |
443 ASSERT_TRUE(msg.has_agc_enabled()); | |
444 EXPECT_EQ(AudioProcessing::kNoError, | |
445 apm_->gain_control()->Enable(msg.agc_enabled())); | |
446 | |
447 ASSERT_TRUE(msg.has_agc_mode()); | |
448 EXPECT_EQ(AudioProcessing::kNoError, | |
449 apm_->gain_control()->set_mode( | |
450 static_cast<GainControl::Mode>(msg.agc_mode()))); | |
451 | |
452 ASSERT_TRUE(msg.has_agc_limiter_enabled()); | |
453 EXPECT_EQ(AudioProcessing::kNoError, | |
454 apm_->gain_control()->enable_limiter(msg.agc_limiter_enabled())); | |
455 | |
456 // HPF configs. | |
457 ASSERT_TRUE(msg.has_hpf_enabled()); | |
458 EXPECT_EQ(AudioProcessing::kNoError, | |
459 apm_->high_pass_filter()->Enable(msg.hpf_enabled())); | |
460 | |
461 // NS configs. | |
462 ASSERT_TRUE(msg.has_ns_enabled()); | |
463 EXPECT_EQ(AudioProcessing::kNoError, | |
464 apm_->noise_suppression()->Enable(msg.ns_enabled())); | |
465 | |
466 ASSERT_TRUE(msg.has_ns_level()); | |
467 EXPECT_EQ(AudioProcessing::kNoError, | |
468 apm_->noise_suppression()->set_level( | |
469 static_cast<NoiseSuppression::Level>(msg.ns_level()))); | |
470 } | |
471 | |
472 TEST_F(DebugDumpTest, SimpleCase) { | 265 TEST_F(DebugDumpTest, SimpleCase) { |
473 Config config; | 266 Config config; |
474 DebugDumpGenerator generator(config); | 267 DebugDumpGenerator generator(config); |
475 generator.StartRecording(); | 268 generator.StartRecording(); |
476 generator.Process(100); | 269 generator.Process(100); |
477 generator.StopRecording(); | 270 generator.StopRecording(); |
478 VerifyDebugDump(generator.dump_file_name()); | 271 VerifyDebugDump(generator.dump_file_name()); |
479 } | 272 } |
480 | 273 |
481 TEST_F(DebugDumpTest, ChangeInputFormat) { | 274 TEST_F(DebugDumpTest, ChangeInputFormat) { |
(...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
604 config.Set<ExperimentalNs>(new ExperimentalNs(true)); | 397 config.Set<ExperimentalNs>(new ExperimentalNs(true)); |
605 DebugDumpGenerator generator(config); | 398 DebugDumpGenerator generator(config); |
606 generator.StartRecording(); | 399 generator.StartRecording(); |
607 generator.Process(100); | 400 generator.Process(100); |
608 generator.StopRecording(); | 401 generator.StopRecording(); |
609 VerifyDebugDump(generator.dump_file_name()); | 402 VerifyDebugDump(generator.dump_file_name()); |
610 } | 403 } |
611 | 404 |
612 } // namespace test | 405 } // namespace test |
613 } // namespace webrtc | 406 } // namespace webrtc |
OLD | NEW |