2 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
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.
20 #include "webrtc/common.h"
21 #include "webrtc/modules/audio_processing/include/audio_processing.h"
22 #include "webrtc/modules/interface/module_common_types.h"
23 #include "webrtc/system_wrappers/interface/cpu_features_wrapper.h"
24 #include "webrtc/system_wrappers/interface/scoped_ptr.h"
25 #include "webrtc/system_wrappers/interface/tick_util.h"
26 #include "webrtc/test/testsupport/fileutils.h"
27 #include "webrtc/test/testsupport/perf_test.h"
28 #ifdef WEBRTC_ANDROID_PLATFORM_BUILD
29 #include "gtest/gtest.h"
30 #include "external/webrtc/webrtc/modules/audio_processing/debug.pb.h"
32 #include "testing/gtest/include/gtest/gtest.h"
33 #include "webrtc/audio_processing/debug.pb.h"
36 using webrtc::AudioFrame;
37 using webrtc::AudioProcessing;
39 using webrtc::DelayCorrection;
40 using webrtc::EchoCancellation;
41 using webrtc::GainControl;
42 using webrtc::NoiseSuppression;
43 using webrtc::scoped_array;
44 using webrtc::scoped_ptr;
45 using webrtc::TickInterval;
46 using webrtc::TickTime;
47 using webrtc::VoiceDetection;
49 using webrtc::audioproc::Event;
50 using webrtc::audioproc::Init;
51 using webrtc::audioproc::ReverseStream;
52 using webrtc::audioproc::Stream;
55 // Returns true on success, false on error or end-of-file.
56 bool ReadMessageFromFile(FILE* file,
57 ::google::protobuf::MessageLite* msg) {
58 // The "wire format" for the size is little-endian.
59 // Assume process_test is running on a little-endian machine.
61 if (fread(&size, sizeof(int32_t), 1, file) != 1) {
67 const size_t usize = static_cast<size_t>(size);
69 scoped_array<char> array(new char[usize]);
70 if (fread(array.get(), sizeof(char), usize, file) != usize) {
75 return msg->ParseFromArray(array.get(), usize);
78 void PrintStat(const AudioProcessing::Statistic& stat) {
79 printf("%d, %d, %d\n", stat.average,
86 "Usage: process_test [options] [-pb PROTOBUF_FILE]\n"
87 " [-ir REVERSE_FILE] [-i PRIMARY_FILE] [-o OUT_FILE]\n");
89 "process_test is a test application for AudioProcessing.\n\n"
90 "When a protobuf debug file is available, specify it with -pb.\n"
91 "Alternately, when -ir or -i is used, the specified files will be\n"
92 "processed directly in a simulation mode. Otherwise the full set of\n"
93 "legacy test files is expected to be present in the working directory.\n");
96 printf("General configuration (only used for the simulation mode):\n");
97 printf(" -fs SAMPLE_RATE_HZ\n");
98 printf(" -ch CHANNELS_IN CHANNELS_OUT\n");
99 printf(" -rch REVERSE_CHANNELS\n");
101 printf("Component configuration:\n");
103 "All components are disabled by default. Each block below begins with a\n"
104 "flag to enable the component with default settings. The subsequent flags\n"
105 "in the block are used to provide configuration settings.\n");
106 printf("\n -aec Echo cancellation\n");
107 printf(" --drift_compensation\n");
108 printf(" --no_drift_compensation\n");
109 printf(" --no_echo_metrics\n");
110 printf(" --no_delay_logging\n");
111 printf(" --aec_suppression_level LEVEL [0 - 2]\n");
112 printf(" --extended_filter\n");
113 printf("\n -aecm Echo control mobile\n");
114 printf(" --aecm_echo_path_in_file FILE\n");
115 printf(" --aecm_echo_path_out_file FILE\n");
116 printf(" --no_comfort_noise\n");
117 printf(" --routing_mode MODE [0 - 4]\n");
118 printf("\n -agc Gain control\n");
119 printf(" --analog\n");
120 printf(" --adaptive_digital\n");
121 printf(" --fixed_digital\n");
122 printf(" --target_level LEVEL\n");
123 printf(" --compression_gain GAIN\n");
124 printf(" --limiter\n");
125 printf(" --no_limiter\n");
126 printf("\n -hpf High pass filter\n");
127 printf("\n -ns Noise suppression\n");
128 printf(" --ns_low\n");
129 printf(" --ns_moderate\n");
130 printf(" --ns_high\n");
131 printf(" --ns_very_high\n");
132 printf(" --ns_prob_file FILE\n");
133 printf("\n -vad Voice activity detection\n");
134 printf(" --vad_out_file FILE\n");
135 printf("\n Level metrics (enabled by default)\n");
136 printf(" --no_level_metrics\n");
138 printf("Modifiers:\n");
139 printf(" --noasm Disable SSE optimization.\n");
140 printf(" --add_delay DELAY Add DELAY ms to input value.\n");
141 printf(" --delay DELAY Override input delay with DELAY ms.\n");
142 printf(" --perf Measure performance.\n");
143 printf(" --quiet Suppress text output.\n");
144 printf(" --no_progress Suppress progress.\n");
145 printf(" --debug_file FILE Dump a debug recording.\n");
148 static float MicLevel2Gain(int level) {
149 return pow(10.0f, ((level - 127.0f) / 128.0f * 40.0f) / 20.0f);
152 static void SimulateMic(int mic_level, AudioFrame* frame) {
153 mic_level = std::min(std::max(mic_level, 0), 255);
154 float mic_gain = MicLevel2Gain(mic_level);
155 int num_samples = frame->samples_per_channel_ * frame->num_channels_;
157 for (int n = 0; n < num_samples; n++) {
158 v = floor(frame->data_[n] * mic_gain + 0.5);
159 v = std::max(std::min(32767.0f, v), -32768.0f);
160 frame->data_[n] = static_cast<int16_t>(v);
164 // void function for gtest.
165 void void_main(int argc, char* argv[]) {
166 if (argc > 1 && strcmp(argv[1], "--help") == 0) {
172 printf("Did you mean to run without arguments?\n");
173 printf("Try `process_test --help' for more information.\n\n");
176 scoped_ptr<AudioProcessing> apm(AudioProcessing::Create(0));
177 ASSERT_TRUE(apm.get() != NULL);
179 const char* pb_filename = NULL;
180 const char* far_filename = NULL;
181 const char* near_filename = NULL;
182 const char* out_filename = NULL;
183 const char* vad_out_filename = NULL;
184 const char* ns_prob_filename = NULL;
185 const char* aecm_echo_path_in_filename = NULL;
186 const char* aecm_echo_path_out_filename = NULL;
188 int32_t sample_rate_hz = 16000;
189 int32_t device_sample_rate_hz = 16000;
191 int num_capture_input_channels = 1;
192 int num_capture_output_channels = 1;
193 int num_render_channels = 1;
195 int samples_per_channel = sample_rate_hz / 100;
197 bool simulating = false;
198 bool perf_testing = false;
200 bool progress = true;
201 int extra_delay_ms = 0;
202 int override_delay_ms = 0;
203 //bool interleaved = true;
205 ASSERT_EQ(apm->kNoError, apm->level_estimator()->Enable(true));
206 for (int i = 1; i < argc; i++) {
207 if (strcmp(argv[i], "-pb") == 0) {
209 ASSERT_LT(i, argc) << "Specify protobuf filename after -pb";
210 pb_filename = argv[i];
212 } else if (strcmp(argv[i], "-ir") == 0) {
214 ASSERT_LT(i, argc) << "Specify filename after -ir";
215 far_filename = argv[i];
218 } else if (strcmp(argv[i], "-i") == 0) {
220 ASSERT_LT(i, argc) << "Specify filename after -i";
221 near_filename = argv[i];
224 } else if (strcmp(argv[i], "-o") == 0) {
226 ASSERT_LT(i, argc) << "Specify filename after -o";
227 out_filename = argv[i];
229 } else if (strcmp(argv[i], "-fs") == 0) {
231 ASSERT_LT(i, argc) << "Specify sample rate after -fs";
232 ASSERT_EQ(1, sscanf(argv[i], "%d", &sample_rate_hz));
233 samples_per_channel = sample_rate_hz / 100;
235 ASSERT_EQ(apm->kNoError,
236 apm->set_sample_rate_hz(sample_rate_hz));
238 } else if (strcmp(argv[i], "-ch") == 0) {
240 ASSERT_LT(i + 1, argc) << "Specify number of channels after -ch";
241 ASSERT_EQ(1, sscanf(argv[i], "%d", &num_capture_input_channels));
243 ASSERT_EQ(1, sscanf(argv[i], "%d", &num_capture_output_channels));
245 ASSERT_EQ(apm->kNoError,
246 apm->set_num_channels(num_capture_input_channels,
247 num_capture_output_channels));
249 } else if (strcmp(argv[i], "-rch") == 0) {
251 ASSERT_LT(i, argc) << "Specify number of channels after -rch";
252 ASSERT_EQ(1, sscanf(argv[i], "%d", &num_render_channels));
254 ASSERT_EQ(apm->kNoError,
255 apm->set_num_reverse_channels(num_render_channels));
257 } else if (strcmp(argv[i], "-aec") == 0) {
258 ASSERT_EQ(apm->kNoError, apm->echo_cancellation()->Enable(true));
259 ASSERT_EQ(apm->kNoError,
260 apm->echo_cancellation()->enable_metrics(true));
261 ASSERT_EQ(apm->kNoError,
262 apm->echo_cancellation()->enable_delay_logging(true));
264 } else if (strcmp(argv[i], "--drift_compensation") == 0) {
265 ASSERT_EQ(apm->kNoError, apm->echo_cancellation()->Enable(true));
266 // TODO(ajm): this is enabled in the VQE test app by default. Investigate
267 // why it can give better performance despite passing zeros.
268 ASSERT_EQ(apm->kNoError,
269 apm->echo_cancellation()->enable_drift_compensation(true));
270 } else if (strcmp(argv[i], "--no_drift_compensation") == 0) {
271 ASSERT_EQ(apm->kNoError, apm->echo_cancellation()->Enable(true));
272 ASSERT_EQ(apm->kNoError,
273 apm->echo_cancellation()->enable_drift_compensation(false));
275 } else if (strcmp(argv[i], "--no_echo_metrics") == 0) {
276 ASSERT_EQ(apm->kNoError, apm->echo_cancellation()->Enable(true));
277 ASSERT_EQ(apm->kNoError,
278 apm->echo_cancellation()->enable_metrics(false));
280 } else if (strcmp(argv[i], "--no_delay_logging") == 0) {
281 ASSERT_EQ(apm->kNoError, apm->echo_cancellation()->Enable(true));
282 ASSERT_EQ(apm->kNoError,
283 apm->echo_cancellation()->enable_delay_logging(false));
285 } else if (strcmp(argv[i], "--no_level_metrics") == 0) {
286 ASSERT_EQ(apm->kNoError, apm->level_estimator()->Enable(false));
288 } else if (strcmp(argv[i], "--aec_suppression_level") == 0) {
290 ASSERT_LT(i, argc) << "Specify level after --aec_suppression_level";
291 int suppression_level;
292 ASSERT_EQ(1, sscanf(argv[i], "%d", &suppression_level));
293 ASSERT_EQ(apm->kNoError,
294 apm->echo_cancellation()->set_suppression_level(
295 static_cast<webrtc::EchoCancellation::SuppressionLevel>(
296 suppression_level)));
298 } else if (strcmp(argv[i], "--extended_filter") == 0) {
300 config.Set<DelayCorrection>(new DelayCorrection(true));
301 apm->SetExtraOptions(config);
303 } else if (strcmp(argv[i], "-aecm") == 0) {
304 ASSERT_EQ(apm->kNoError, apm->echo_control_mobile()->Enable(true));
306 } else if (strcmp(argv[i], "--aecm_echo_path_in_file") == 0) {
308 ASSERT_LT(i, argc) << "Specify filename after --aecm_echo_path_in_file";
309 aecm_echo_path_in_filename = argv[i];
311 } else if (strcmp(argv[i], "--aecm_echo_path_out_file") == 0) {
313 ASSERT_LT(i, argc) << "Specify filename after --aecm_echo_path_out_file";
314 aecm_echo_path_out_filename = argv[i];
316 } else if (strcmp(argv[i], "--no_comfort_noise") == 0) {
317 ASSERT_EQ(apm->kNoError,
318 apm->echo_control_mobile()->enable_comfort_noise(false));
320 } else if (strcmp(argv[i], "--routing_mode") == 0) {
322 ASSERT_LT(i, argc) << "Specify mode after --routing_mode";
324 ASSERT_EQ(1, sscanf(argv[i], "%d", &routing_mode));
325 ASSERT_EQ(apm->kNoError,
326 apm->echo_control_mobile()->set_routing_mode(
327 static_cast<webrtc::EchoControlMobile::RoutingMode>(
330 } else if (strcmp(argv[i], "-agc") == 0) {
331 ASSERT_EQ(apm->kNoError, apm->gain_control()->Enable(true));
333 } else if (strcmp(argv[i], "--analog") == 0) {
334 ASSERT_EQ(apm->kNoError, apm->gain_control()->Enable(true));
335 ASSERT_EQ(apm->kNoError,
336 apm->gain_control()->set_mode(GainControl::kAdaptiveAnalog));
338 } else if (strcmp(argv[i], "--adaptive_digital") == 0) {
339 ASSERT_EQ(apm->kNoError, apm->gain_control()->Enable(true));
340 ASSERT_EQ(apm->kNoError,
341 apm->gain_control()->set_mode(GainControl::kAdaptiveDigital));
343 } else if (strcmp(argv[i], "--fixed_digital") == 0) {
344 ASSERT_EQ(apm->kNoError, apm->gain_control()->Enable(true));
345 ASSERT_EQ(apm->kNoError,
346 apm->gain_control()->set_mode(GainControl::kFixedDigital));
348 } else if (strcmp(argv[i], "--target_level") == 0) {
351 ASSERT_EQ(1, sscanf(argv[i], "%d", &level));
353 ASSERT_EQ(apm->kNoError, apm->gain_control()->Enable(true));
354 ASSERT_EQ(apm->kNoError,
355 apm->gain_control()->set_target_level_dbfs(level));
357 } else if (strcmp(argv[i], "--compression_gain") == 0) {
360 ASSERT_EQ(1, sscanf(argv[i], "%d", &gain));
362 ASSERT_EQ(apm->kNoError, apm->gain_control()->Enable(true));
363 ASSERT_EQ(apm->kNoError,
364 apm->gain_control()->set_compression_gain_db(gain));
366 } else if (strcmp(argv[i], "--limiter") == 0) {
367 ASSERT_EQ(apm->kNoError, apm->gain_control()->Enable(true));
368 ASSERT_EQ(apm->kNoError,
369 apm->gain_control()->enable_limiter(true));
371 } else if (strcmp(argv[i], "--no_limiter") == 0) {
372 ASSERT_EQ(apm->kNoError, apm->gain_control()->Enable(true));
373 ASSERT_EQ(apm->kNoError,
374 apm->gain_control()->enable_limiter(false));
376 } else if (strcmp(argv[i], "-hpf") == 0) {
377 ASSERT_EQ(apm->kNoError, apm->high_pass_filter()->Enable(true));
379 } else if (strcmp(argv[i], "-ns") == 0) {
380 ASSERT_EQ(apm->kNoError, apm->noise_suppression()->Enable(true));
382 } else if (strcmp(argv[i], "--ns_low") == 0) {
383 ASSERT_EQ(apm->kNoError, apm->noise_suppression()->Enable(true));
384 ASSERT_EQ(apm->kNoError,
385 apm->noise_suppression()->set_level(NoiseSuppression::kLow));
387 } else if (strcmp(argv[i], "--ns_moderate") == 0) {
388 ASSERT_EQ(apm->kNoError, apm->noise_suppression()->Enable(true));
389 ASSERT_EQ(apm->kNoError,
390 apm->noise_suppression()->set_level(NoiseSuppression::kModerate));
392 } else if (strcmp(argv[i], "--ns_high") == 0) {
393 ASSERT_EQ(apm->kNoError, apm->noise_suppression()->Enable(true));
394 ASSERT_EQ(apm->kNoError,
395 apm->noise_suppression()->set_level(NoiseSuppression::kHigh));
397 } else if (strcmp(argv[i], "--ns_very_high") == 0) {
398 ASSERT_EQ(apm->kNoError, apm->noise_suppression()->Enable(true));
399 ASSERT_EQ(apm->kNoError,
400 apm->noise_suppression()->set_level(NoiseSuppression::kVeryHigh));
402 } else if (strcmp(argv[i], "--ns_prob_file") == 0) {
404 ASSERT_LT(i, argc) << "Specify filename after --ns_prob_file";
405 ns_prob_filename = argv[i];
407 } else if (strcmp(argv[i], "-vad") == 0) {
408 ASSERT_EQ(apm->kNoError, apm->voice_detection()->Enable(true));
410 } else if (strcmp(argv[i], "--vad_very_low") == 0) {
411 ASSERT_EQ(apm->kNoError, apm->voice_detection()->Enable(true));
412 ASSERT_EQ(apm->kNoError,
413 apm->voice_detection()->set_likelihood(
414 VoiceDetection::kVeryLowLikelihood));
416 } else if (strcmp(argv[i], "--vad_low") == 0) {
417 ASSERT_EQ(apm->kNoError, apm->voice_detection()->Enable(true));
418 ASSERT_EQ(apm->kNoError,
419 apm->voice_detection()->set_likelihood(
420 VoiceDetection::kLowLikelihood));
422 } else if (strcmp(argv[i], "--vad_moderate") == 0) {
423 ASSERT_EQ(apm->kNoError, apm->voice_detection()->Enable(true));
424 ASSERT_EQ(apm->kNoError,
425 apm->voice_detection()->set_likelihood(
426 VoiceDetection::kModerateLikelihood));
428 } else if (strcmp(argv[i], "--vad_high") == 0) {
429 ASSERT_EQ(apm->kNoError, apm->voice_detection()->Enable(true));
430 ASSERT_EQ(apm->kNoError,
431 apm->voice_detection()->set_likelihood(
432 VoiceDetection::kHighLikelihood));
434 } else if (strcmp(argv[i], "--vad_out_file") == 0) {
436 ASSERT_LT(i, argc) << "Specify filename after --vad_out_file";
437 vad_out_filename = argv[i];
439 } else if (strcmp(argv[i], "--noasm") == 0) {
440 WebRtc_GetCPUInfo = WebRtc_GetCPUInfoNoASM;
441 // We need to reinitialize here if components have already been enabled.
442 ASSERT_EQ(apm->kNoError, apm->Initialize());
444 } else if (strcmp(argv[i], "--add_delay") == 0) {
446 ASSERT_EQ(1, sscanf(argv[i], "%d", &extra_delay_ms));
448 } else if (strcmp(argv[i], "--delay") == 0) {
450 ASSERT_EQ(1, sscanf(argv[i], "%d", &override_delay_ms));
452 } else if (strcmp(argv[i], "--perf") == 0) {
455 } else if (strcmp(argv[i], "--quiet") == 0) {
459 } else if (strcmp(argv[i], "--no_progress") == 0) {
462 } else if (strcmp(argv[i], "--debug_file") == 0) {
464 ASSERT_LT(i, argc) << "Specify filename after --debug_file";
465 ASSERT_EQ(apm->kNoError, apm->StartDebugRecording(argv[i]));
467 FAIL() << "Unrecognized argument " << argv[i];
470 // If we're reading a protobuf file, ensure a simulation hasn't also
471 // been requested (which makes no sense...)
472 ASSERT_FALSE(pb_filename && simulating);
475 printf("Sample rate: %d Hz\n", sample_rate_hz);
476 printf("Primary channels: %d (in), %d (out)\n",
477 num_capture_input_channels,
478 num_capture_output_channels);
479 printf("Reverse channels: %d \n", num_render_channels);
482 const std::string out_path = webrtc::test::OutputPath();
483 const char far_file_default[] = "apm_far.pcm";
484 const char near_file_default[] = "apm_near.pcm";
485 const std::string out_file_default = out_path + "out.pcm";
486 const char event_filename[] = "apm_event.dat";
487 const char delay_filename[] = "apm_delay.dat";
488 const char drift_filename[] = "apm_drift.dat";
489 const std::string vad_file_default = out_path + "vad_out.dat";
490 const std::string ns_prob_file_default = out_path + "ns_prob.dat";
493 far_filename = far_file_default;
494 near_filename = near_file_default;
498 out_filename = out_file_default.c_str();
501 if (!vad_out_filename) {
502 vad_out_filename = vad_file_default.c_str();
505 if (!ns_prob_filename) {
506 ns_prob_filename = ns_prob_file_default.c_str();
509 FILE* pb_file = NULL;
510 FILE* far_file = NULL;
511 FILE* near_file = NULL;
512 FILE* out_file = NULL;
513 FILE* event_file = NULL;
514 FILE* delay_file = NULL;
515 FILE* drift_file = NULL;
516 FILE* vad_out_file = NULL;
517 FILE* ns_prob_file = NULL;
518 FILE* aecm_echo_path_in_file = NULL;
519 FILE* aecm_echo_path_out_file = NULL;
522 pb_file = fopen(pb_filename, "rb");
523 ASSERT_TRUE(NULL != pb_file) << "Unable to open protobuf file "
527 far_file = fopen(far_filename, "rb");
528 ASSERT_TRUE(NULL != far_file) << "Unable to open far-end audio file "
532 near_file = fopen(near_filename, "rb");
533 ASSERT_TRUE(NULL != near_file) << "Unable to open near-end audio file "
536 event_file = fopen(event_filename, "rb");
537 ASSERT_TRUE(NULL != event_file) << "Unable to open event file "
540 delay_file = fopen(delay_filename, "rb");
541 ASSERT_TRUE(NULL != delay_file) << "Unable to open buffer file "
544 drift_file = fopen(drift_filename, "rb");
545 ASSERT_TRUE(NULL != drift_file) << "Unable to open drift file "
550 out_file = fopen(out_filename, "wb");
551 ASSERT_TRUE(NULL != out_file) << "Unable to open output audio file "
554 int near_size_bytes = 0;
557 stat(pb_filename, &st);
558 // Crude estimate, but should be good enough.
559 near_size_bytes = st.st_size / 3;
562 stat(near_filename, &st);
563 near_size_bytes = st.st_size;
566 if (apm->voice_detection()->is_enabled()) {
567 vad_out_file = fopen(vad_out_filename, "wb");
568 ASSERT_TRUE(NULL != vad_out_file) << "Unable to open VAD output file "
572 if (apm->noise_suppression()->is_enabled()) {
573 ns_prob_file = fopen(ns_prob_filename, "wb");
574 ASSERT_TRUE(NULL != ns_prob_file) << "Unable to open NS output file "
578 if (aecm_echo_path_in_filename != NULL) {
579 aecm_echo_path_in_file = fopen(aecm_echo_path_in_filename, "rb");
580 ASSERT_TRUE(NULL != aecm_echo_path_in_file) << "Unable to open file "
581 << aecm_echo_path_in_filename;
583 const size_t path_size =
584 apm->echo_control_mobile()->echo_path_size_bytes();
585 scoped_array<char> echo_path(new char[path_size]);
586 ASSERT_EQ(path_size, fread(echo_path.get(),
589 aecm_echo_path_in_file));
590 EXPECT_EQ(apm->kNoError,
591 apm->echo_control_mobile()->SetEchoPath(echo_path.get(),
593 fclose(aecm_echo_path_in_file);
594 aecm_echo_path_in_file = NULL;
597 if (aecm_echo_path_out_filename != NULL) {
598 aecm_echo_path_out_file = fopen(aecm_echo_path_out_filename, "wb");
599 ASSERT_TRUE(NULL != aecm_echo_path_out_file) << "Unable to open file "
600 << aecm_echo_path_out_filename;
603 size_t read_count = 0;
604 int reverse_count = 0;
605 int primary_count = 0;
606 int near_read_bytes = 0;
607 TickInterval acc_ticks;
609 AudioFrame far_frame;
610 AudioFrame near_frame;
613 int drift_samples = 0;
614 int capture_level = 127;
615 int8_t stream_has_voice = 0;
616 float ns_speech_prob = 0.0f;
618 TickTime t0 = TickTime::Now();
620 int64_t max_time_us = 0;
621 int64_t max_time_reverse_us = 0;
622 int64_t min_time_us = 1e6;
623 int64_t min_time_reverse_us = 1e6;
625 // TODO(ajm): Ideally we would refactor this block into separate functions,
626 // but for now we want to share the variables.
629 while (ReadMessageFromFile(pb_file, &event_msg)) {
630 std::ostringstream trace_stream;
631 trace_stream << "Processed frames: " << reverse_count << " (reverse), "
632 << primary_count << " (primary)";
633 SCOPED_TRACE(trace_stream.str());
635 if (event_msg.type() == Event::INIT) {
636 ASSERT_TRUE(event_msg.has_init());
637 const Init msg = event_msg.init();
639 ASSERT_TRUE(msg.has_sample_rate());
640 ASSERT_EQ(apm->kNoError,
641 apm->set_sample_rate_hz(msg.sample_rate()));
643 ASSERT_TRUE(msg.has_device_sample_rate());
644 ASSERT_EQ(apm->kNoError,
645 apm->echo_cancellation()->set_device_sample_rate_hz(
646 msg.device_sample_rate()));
648 ASSERT_TRUE(msg.has_num_input_channels());
649 ASSERT_TRUE(msg.has_num_output_channels());
650 ASSERT_EQ(apm->kNoError,
651 apm->set_num_channels(msg.num_input_channels(),
652 msg.num_output_channels()));
654 ASSERT_TRUE(msg.has_num_reverse_channels());
655 ASSERT_EQ(apm->kNoError,
656 apm->set_num_reverse_channels(msg.num_reverse_channels()));
658 samples_per_channel = msg.sample_rate() / 100;
659 far_frame.sample_rate_hz_ = msg.sample_rate();
660 far_frame.samples_per_channel_ = samples_per_channel;
661 far_frame.num_channels_ = msg.num_reverse_channels();
662 near_frame.sample_rate_hz_ = msg.sample_rate();
663 near_frame.samples_per_channel_ = samples_per_channel;
664 near_frame.num_channels_ = msg.num_input_channels();
667 printf("Init at frame: %d (primary), %d (reverse)\n",
668 primary_count, reverse_count);
669 printf(" Sample rate: %d Hz\n", msg.sample_rate());
670 printf(" Primary channels: %d (in), %d (out)\n",
671 msg.num_input_channels(),
672 msg.num_output_channels());
673 printf(" Reverse channels: %d \n", msg.num_reverse_channels());
676 } else if (event_msg.type() == Event::REVERSE_STREAM) {
677 ASSERT_TRUE(event_msg.has_reverse_stream());
678 const ReverseStream msg = event_msg.reverse_stream();
681 ASSERT_TRUE(msg.has_data());
682 ASSERT_EQ(sizeof(int16_t) * samples_per_channel *
683 far_frame.num_channels_, msg.data().size());
684 memcpy(far_frame.data_, msg.data().data(), msg.data().size());
687 t0 = TickTime::Now();
690 ASSERT_EQ(apm->kNoError,
691 apm->AnalyzeReverseStream(&far_frame));
694 t1 = TickTime::Now();
695 TickInterval tick_diff = t1 - t0;
696 acc_ticks += tick_diff;
697 if (tick_diff.Microseconds() > max_time_reverse_us) {
698 max_time_reverse_us = tick_diff.Microseconds();
700 if (tick_diff.Microseconds() < min_time_reverse_us) {
701 min_time_reverse_us = tick_diff.Microseconds();
705 } else if (event_msg.type() == Event::STREAM) {
706 ASSERT_TRUE(event_msg.has_stream());
707 const Stream msg = event_msg.stream();
710 // ProcessStream could have changed this for the output frame.
711 near_frame.num_channels_ = apm->num_input_channels();
713 ASSERT_TRUE(msg.has_input_data());
714 ASSERT_EQ(sizeof(int16_t) * samples_per_channel *
715 near_frame.num_channels_, msg.input_data().size());
716 memcpy(near_frame.data_,
717 msg.input_data().data(),
718 msg.input_data().size());
720 near_read_bytes += msg.input_data().size();
721 if (progress && primary_count % 100 == 0) {
722 printf("%.0f%% complete\r",
723 (near_read_bytes * 100.0) / near_size_bytes);
728 t0 = TickTime::Now();
731 ASSERT_EQ(apm->kNoError,
732 apm->gain_control()->set_stream_analog_level(msg.level()));
733 delay_ms = msg.delay() + extra_delay_ms;
734 if (override_delay_ms) {
735 delay_ms = override_delay_ms;
737 ASSERT_EQ(apm->kNoError,
738 apm->set_stream_delay_ms(delay_ms));
739 apm->echo_cancellation()->set_stream_drift_samples(msg.drift());
741 int err = apm->ProcessStream(&near_frame);
742 if (err == apm->kBadStreamParameterWarning) {
743 printf("Bad parameter warning. %s\n", trace_stream.str().c_str());
745 ASSERT_TRUE(err == apm->kNoError ||
746 err == apm->kBadStreamParameterWarning);
747 ASSERT_TRUE(near_frame.num_channels_ == apm->num_output_channels());
750 static_cast<int8_t>(apm->voice_detection()->stream_has_voice());
751 if (vad_out_file != NULL) {
752 ASSERT_EQ(1u, fwrite(&stream_has_voice,
753 sizeof(stream_has_voice),
758 if (ns_prob_file != NULL) {
759 ns_speech_prob = apm->noise_suppression()->speech_probability();
760 ASSERT_EQ(1u, fwrite(&ns_speech_prob,
761 sizeof(ns_speech_prob),
767 t1 = TickTime::Now();
768 TickInterval tick_diff = t1 - t0;
769 acc_ticks += tick_diff;
770 if (tick_diff.Microseconds() > max_time_us) {
771 max_time_us = tick_diff.Microseconds();
773 if (tick_diff.Microseconds() < min_time_us) {
774 min_time_us = tick_diff.Microseconds();
778 size_t size = samples_per_channel * near_frame.num_channels_;
779 ASSERT_EQ(size, fwrite(near_frame.data_,
786 ASSERT_TRUE(feof(pb_file));
793 kResetEventDeprecated
796 while (simulating || feof(event_file) == 0) {
797 std::ostringstream trace_stream;
798 trace_stream << "Processed frames: " << reverse_count << " (reverse), "
799 << primary_count << " (primary)";
800 SCOPED_TRACE(trace_stream.str());
803 if (far_file == NULL) {
804 event = kCaptureEvent;
806 if (event == kRenderEvent) {
807 event = kCaptureEvent;
809 event = kRenderEvent;
813 read_count = fread(&event, sizeof(event), 1, event_file);
814 if (read_count != 1) {
819 far_frame.sample_rate_hz_ = sample_rate_hz;
820 far_frame.samples_per_channel_ = samples_per_channel;
821 far_frame.num_channels_ = num_render_channels;
822 near_frame.sample_rate_hz_ = sample_rate_hz;
823 near_frame.samples_per_channel_ = samples_per_channel;
825 if (event == kInitializeEvent || event == kResetEventDeprecated) {
827 fread(&sample_rate_hz, sizeof(sample_rate_hz), 1, event_file));
828 samples_per_channel = sample_rate_hz / 100;
831 fread(&device_sample_rate_hz,
832 sizeof(device_sample_rate_hz),
836 ASSERT_EQ(apm->kNoError,
837 apm->set_sample_rate_hz(sample_rate_hz));
839 ASSERT_EQ(apm->kNoError,
840 apm->echo_cancellation()->set_device_sample_rate_hz(
841 device_sample_rate_hz));
843 far_frame.sample_rate_hz_ = sample_rate_hz;
844 far_frame.samples_per_channel_ = samples_per_channel;
845 far_frame.num_channels_ = num_render_channels;
846 near_frame.sample_rate_hz_ = sample_rate_hz;
847 near_frame.samples_per_channel_ = samples_per_channel;
850 printf("Init at frame: %d (primary), %d (reverse)\n",
851 primary_count, reverse_count);
852 printf(" Sample rate: %d Hz\n", sample_rate_hz);
855 } else if (event == kRenderEvent) {
858 size_t size = samples_per_channel * num_render_channels;
859 read_count = fread(far_frame.data_,
865 if (read_count != size) {
866 // Read an equal amount from the near file to avoid errors due to
867 // not reaching end-of-file.
868 EXPECT_EQ(0, fseek(near_file, read_count * sizeof(int16_t),
870 break; // This is expected.
873 ASSERT_EQ(size, read_count);
877 t0 = TickTime::Now();
880 ASSERT_EQ(apm->kNoError,
881 apm->AnalyzeReverseStream(&far_frame));
884 t1 = TickTime::Now();
885 TickInterval tick_diff = t1 - t0;
886 acc_ticks += tick_diff;
887 if (tick_diff.Microseconds() > max_time_reverse_us) {
888 max_time_reverse_us = tick_diff.Microseconds();
890 if (tick_diff.Microseconds() < min_time_reverse_us) {
891 min_time_reverse_us = tick_diff.Microseconds();
895 } else if (event == kCaptureEvent) {
897 near_frame.num_channels_ = num_capture_input_channels;
899 size_t size = samples_per_channel * num_capture_input_channels;
900 read_count = fread(near_frame.data_,
905 near_read_bytes += read_count * sizeof(int16_t);
906 if (progress && primary_count % 100 == 0) {
907 printf("%.0f%% complete\r",
908 (near_read_bytes * 100.0) / near_size_bytes);
912 if (read_count != size) {
913 break; // This is expected.
919 ASSERT_EQ(size, read_count);
921 // TODO(ajm): sizeof(delay_ms) for current files?
923 fread(&delay_ms, 2, 1, delay_file));
925 fread(&drift_samples, sizeof(drift_samples), 1, drift_file));
928 if (apm->gain_control()->is_enabled() &&
929 apm->gain_control()->mode() == GainControl::kAdaptiveAnalog) {
930 SimulateMic(capture_level, &near_frame);
934 t0 = TickTime::Now();
937 const int capture_level_in = capture_level;
938 ASSERT_EQ(apm->kNoError,
939 apm->gain_control()->set_stream_analog_level(capture_level));
940 delay_ms += extra_delay_ms;
941 if (override_delay_ms) {
942 delay_ms = override_delay_ms;
944 ASSERT_EQ(apm->kNoError,
945 apm->set_stream_delay_ms(delay_ms));
946 apm->echo_cancellation()->set_stream_drift_samples(drift_samples);
948 int err = apm->ProcessStream(&near_frame);
949 if (err == apm->kBadStreamParameterWarning) {
950 printf("Bad parameter warning. %s\n", trace_stream.str().c_str());
952 ASSERT_TRUE(err == apm->kNoError ||
953 err == apm->kBadStreamParameterWarning);
954 ASSERT_TRUE(near_frame.num_channels_ == apm->num_output_channels());
956 capture_level = apm->gain_control()->stream_analog_level();
959 static_cast<int8_t>(apm->voice_detection()->stream_has_voice());
960 if (vad_out_file != NULL) {
961 ASSERT_EQ(1u, fwrite(&stream_has_voice,
962 sizeof(stream_has_voice),
967 if (ns_prob_file != NULL) {
968 ns_speech_prob = apm->noise_suppression()->speech_probability();
969 ASSERT_EQ(1u, fwrite(&ns_speech_prob,
970 sizeof(ns_speech_prob),
975 if (apm->gain_control()->mode() != GainControl::kAdaptiveAnalog) {
976 ASSERT_EQ(capture_level_in, capture_level);
980 t1 = TickTime::Now();
981 TickInterval tick_diff = t1 - t0;
982 acc_ticks += tick_diff;
983 if (tick_diff.Microseconds() > max_time_us) {
984 max_time_us = tick_diff.Microseconds();
986 if (tick_diff.Microseconds() < min_time_us) {
987 min_time_us = tick_diff.Microseconds();
991 size = samples_per_channel * near_frame.num_channels_;
992 ASSERT_EQ(size, fwrite(near_frame.data_,
998 FAIL() << "Event " << event << " is unrecognized";
1002 printf("100%% complete\r");
1004 if (aecm_echo_path_out_file != NULL) {
1005 const size_t path_size =
1006 apm->echo_control_mobile()->echo_path_size_bytes();
1007 scoped_array<char> echo_path(new char[path_size]);
1008 apm->echo_control_mobile()->GetEchoPath(echo_path.get(), path_size);
1009 ASSERT_EQ(path_size, fwrite(echo_path.get(),
1012 aecm_echo_path_out_file));
1013 fclose(aecm_echo_path_out_file);
1014 aecm_echo_path_out_file = NULL;
1018 printf("\nProcessed frames: %d (primary), %d (reverse)\n",
1019 primary_count, reverse_count);
1021 if (apm->level_estimator()->is_enabled()) {
1022 printf("\n--Level metrics--\n");
1023 printf("RMS: %d dBFS\n", -apm->level_estimator()->RMS());
1025 if (apm->echo_cancellation()->are_metrics_enabled()) {
1026 EchoCancellation::Metrics metrics;
1027 apm->echo_cancellation()->GetMetrics(&metrics);
1028 printf("\n--Echo metrics--\n");
1029 printf("(avg, max, min)\n");
1031 PrintStat(metrics.echo_return_loss);
1033 PrintStat(metrics.echo_return_loss_enhancement);
1035 PrintStat(metrics.a_nlp);
1037 if (apm->echo_cancellation()->is_delay_logging_enabled()) {
1040 apm->echo_cancellation()->GetDelayMetrics(&median, &std);
1041 printf("\n--Delay metrics--\n");
1042 printf("Median: %3d\n", median);
1043 printf("Standard deviation: %3d\n", std);
1050 read_count = fread(&temp_int8, sizeof(temp_int8), 1, far_file);
1051 EXPECT_NE(0, feof(far_file)) << "Far-end file not fully processed";
1054 read_count = fread(&temp_int8, sizeof(temp_int8), 1, near_file);
1055 EXPECT_NE(0, feof(near_file)) << "Near-end file not fully processed";
1058 read_count = fread(&temp_int8, sizeof(temp_int8), 1, event_file);
1059 EXPECT_NE(0, feof(event_file)) << "Event file not fully processed";
1060 read_count = fread(&temp_int8, sizeof(temp_int8), 1, delay_file);
1061 EXPECT_NE(0, feof(delay_file)) << "Delay file not fully processed";
1062 read_count = fread(&temp_int8, sizeof(temp_int8), 1, drift_file);
1063 EXPECT_NE(0, feof(drift_file)) << "Drift file not fully processed";
1068 if (primary_count > 0) {
1069 int64_t exec_time = acc_ticks.Milliseconds();
1070 printf("\nTotal time: %.3f s, file time: %.2f s\n",
1071 exec_time * 0.001, primary_count * 0.01);
1072 printf("Time per frame: %.3f ms (average), %.3f ms (max),"
1074 (exec_time * 1.0) / primary_count,
1075 (max_time_us + max_time_reverse_us) / 1000.0,
1076 (min_time_us + min_time_reverse_us) / 1000.0);
1077 // Record the results with Perf test tools.
1078 webrtc::test::PrintResult("audioproc", "", "time_per_10ms_frame",
1079 (exec_time * 1000) / primary_count, "us", false);
1081 printf("Warning: no capture frames\n");
1087 int main(int argc, char* argv[])
1089 void_main(argc, argv);
1091 // Optional, but removes memory leak noise from Valgrind.
1092 google::protobuf::ShutdownProtobufLibrary();