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 } else if (strcmp(argv[i], "-ch") == 0) {
237 ASSERT_LT(i + 1, argc) << "Specify number of channels after -ch";
238 ASSERT_EQ(1, sscanf(argv[i], "%d", &num_capture_input_channels));
240 ASSERT_EQ(1, sscanf(argv[i], "%d", &num_capture_output_channels));
242 } else if (strcmp(argv[i], "-rch") == 0) {
244 ASSERT_LT(i, argc) << "Specify number of channels after -rch";
245 ASSERT_EQ(1, sscanf(argv[i], "%d", &num_render_channels));
247 } else if (strcmp(argv[i], "-aec") == 0) {
248 ASSERT_EQ(apm->kNoError, apm->echo_cancellation()->Enable(true));
249 ASSERT_EQ(apm->kNoError,
250 apm->echo_cancellation()->enable_metrics(true));
251 ASSERT_EQ(apm->kNoError,
252 apm->echo_cancellation()->enable_delay_logging(true));
254 } else if (strcmp(argv[i], "--drift_compensation") == 0) {
255 ASSERT_EQ(apm->kNoError, apm->echo_cancellation()->Enable(true));
256 // TODO(ajm): this is enabled in the VQE test app by default. Investigate
257 // why it can give better performance despite passing zeros.
258 ASSERT_EQ(apm->kNoError,
259 apm->echo_cancellation()->enable_drift_compensation(true));
260 } else if (strcmp(argv[i], "--no_drift_compensation") == 0) {
261 ASSERT_EQ(apm->kNoError, apm->echo_cancellation()->Enable(true));
262 ASSERT_EQ(apm->kNoError,
263 apm->echo_cancellation()->enable_drift_compensation(false));
265 } else if (strcmp(argv[i], "--no_echo_metrics") == 0) {
266 ASSERT_EQ(apm->kNoError, apm->echo_cancellation()->Enable(true));
267 ASSERT_EQ(apm->kNoError,
268 apm->echo_cancellation()->enable_metrics(false));
270 } else if (strcmp(argv[i], "--no_delay_logging") == 0) {
271 ASSERT_EQ(apm->kNoError, apm->echo_cancellation()->Enable(true));
272 ASSERT_EQ(apm->kNoError,
273 apm->echo_cancellation()->enable_delay_logging(false));
275 } else if (strcmp(argv[i], "--no_level_metrics") == 0) {
276 ASSERT_EQ(apm->kNoError, apm->level_estimator()->Enable(false));
278 } else if (strcmp(argv[i], "--aec_suppression_level") == 0) {
280 ASSERT_LT(i, argc) << "Specify level after --aec_suppression_level";
281 int suppression_level;
282 ASSERT_EQ(1, sscanf(argv[i], "%d", &suppression_level));
283 ASSERT_EQ(apm->kNoError,
284 apm->echo_cancellation()->set_suppression_level(
285 static_cast<webrtc::EchoCancellation::SuppressionLevel>(
286 suppression_level)));
288 } else if (strcmp(argv[i], "--extended_filter") == 0) {
290 config.Set<DelayCorrection>(new DelayCorrection(true));
291 apm->SetExtraOptions(config);
293 } else if (strcmp(argv[i], "-aecm") == 0) {
294 ASSERT_EQ(apm->kNoError, apm->echo_control_mobile()->Enable(true));
296 } else if (strcmp(argv[i], "--aecm_echo_path_in_file") == 0) {
298 ASSERT_LT(i, argc) << "Specify filename after --aecm_echo_path_in_file";
299 aecm_echo_path_in_filename = argv[i];
301 } else if (strcmp(argv[i], "--aecm_echo_path_out_file") == 0) {
303 ASSERT_LT(i, argc) << "Specify filename after --aecm_echo_path_out_file";
304 aecm_echo_path_out_filename = argv[i];
306 } else if (strcmp(argv[i], "--no_comfort_noise") == 0) {
307 ASSERT_EQ(apm->kNoError,
308 apm->echo_control_mobile()->enable_comfort_noise(false));
310 } else if (strcmp(argv[i], "--routing_mode") == 0) {
312 ASSERT_LT(i, argc) << "Specify mode after --routing_mode";
314 ASSERT_EQ(1, sscanf(argv[i], "%d", &routing_mode));
315 ASSERT_EQ(apm->kNoError,
316 apm->echo_control_mobile()->set_routing_mode(
317 static_cast<webrtc::EchoControlMobile::RoutingMode>(
320 } else if (strcmp(argv[i], "-agc") == 0) {
321 ASSERT_EQ(apm->kNoError, apm->gain_control()->Enable(true));
323 } else if (strcmp(argv[i], "--analog") == 0) {
324 ASSERT_EQ(apm->kNoError, apm->gain_control()->Enable(true));
325 ASSERT_EQ(apm->kNoError,
326 apm->gain_control()->set_mode(GainControl::kAdaptiveAnalog));
328 } else if (strcmp(argv[i], "--adaptive_digital") == 0) {
329 ASSERT_EQ(apm->kNoError, apm->gain_control()->Enable(true));
330 ASSERT_EQ(apm->kNoError,
331 apm->gain_control()->set_mode(GainControl::kAdaptiveDigital));
333 } else if (strcmp(argv[i], "--fixed_digital") == 0) {
334 ASSERT_EQ(apm->kNoError, apm->gain_control()->Enable(true));
335 ASSERT_EQ(apm->kNoError,
336 apm->gain_control()->set_mode(GainControl::kFixedDigital));
338 } else if (strcmp(argv[i], "--target_level") == 0) {
341 ASSERT_EQ(1, sscanf(argv[i], "%d", &level));
343 ASSERT_EQ(apm->kNoError, apm->gain_control()->Enable(true));
344 ASSERT_EQ(apm->kNoError,
345 apm->gain_control()->set_target_level_dbfs(level));
347 } else if (strcmp(argv[i], "--compression_gain") == 0) {
350 ASSERT_EQ(1, sscanf(argv[i], "%d", &gain));
352 ASSERT_EQ(apm->kNoError, apm->gain_control()->Enable(true));
353 ASSERT_EQ(apm->kNoError,
354 apm->gain_control()->set_compression_gain_db(gain));
356 } else if (strcmp(argv[i], "--limiter") == 0) {
357 ASSERT_EQ(apm->kNoError, apm->gain_control()->Enable(true));
358 ASSERT_EQ(apm->kNoError,
359 apm->gain_control()->enable_limiter(true));
361 } else if (strcmp(argv[i], "--no_limiter") == 0) {
362 ASSERT_EQ(apm->kNoError, apm->gain_control()->Enable(true));
363 ASSERT_EQ(apm->kNoError,
364 apm->gain_control()->enable_limiter(false));
366 } else if (strcmp(argv[i], "-hpf") == 0) {
367 ASSERT_EQ(apm->kNoError, apm->high_pass_filter()->Enable(true));
369 } else if (strcmp(argv[i], "-ns") == 0) {
370 ASSERT_EQ(apm->kNoError, apm->noise_suppression()->Enable(true));
372 } else if (strcmp(argv[i], "--ns_low") == 0) {
373 ASSERT_EQ(apm->kNoError, apm->noise_suppression()->Enable(true));
374 ASSERT_EQ(apm->kNoError,
375 apm->noise_suppression()->set_level(NoiseSuppression::kLow));
377 } else if (strcmp(argv[i], "--ns_moderate") == 0) {
378 ASSERT_EQ(apm->kNoError, apm->noise_suppression()->Enable(true));
379 ASSERT_EQ(apm->kNoError,
380 apm->noise_suppression()->set_level(NoiseSuppression::kModerate));
382 } else if (strcmp(argv[i], "--ns_high") == 0) {
383 ASSERT_EQ(apm->kNoError, apm->noise_suppression()->Enable(true));
384 ASSERT_EQ(apm->kNoError,
385 apm->noise_suppression()->set_level(NoiseSuppression::kHigh));
387 } else if (strcmp(argv[i], "--ns_very_high") == 0) {
388 ASSERT_EQ(apm->kNoError, apm->noise_suppression()->Enable(true));
389 ASSERT_EQ(apm->kNoError,
390 apm->noise_suppression()->set_level(NoiseSuppression::kVeryHigh));
392 } else if (strcmp(argv[i], "--ns_prob_file") == 0) {
394 ASSERT_LT(i, argc) << "Specify filename after --ns_prob_file";
395 ns_prob_filename = argv[i];
397 } else if (strcmp(argv[i], "-vad") == 0) {
398 ASSERT_EQ(apm->kNoError, apm->voice_detection()->Enable(true));
400 } else if (strcmp(argv[i], "--vad_very_low") == 0) {
401 ASSERT_EQ(apm->kNoError, apm->voice_detection()->Enable(true));
402 ASSERT_EQ(apm->kNoError,
403 apm->voice_detection()->set_likelihood(
404 VoiceDetection::kVeryLowLikelihood));
406 } else if (strcmp(argv[i], "--vad_low") == 0) {
407 ASSERT_EQ(apm->kNoError, apm->voice_detection()->Enable(true));
408 ASSERT_EQ(apm->kNoError,
409 apm->voice_detection()->set_likelihood(
410 VoiceDetection::kLowLikelihood));
412 } else if (strcmp(argv[i], "--vad_moderate") == 0) {
413 ASSERT_EQ(apm->kNoError, apm->voice_detection()->Enable(true));
414 ASSERT_EQ(apm->kNoError,
415 apm->voice_detection()->set_likelihood(
416 VoiceDetection::kModerateLikelihood));
418 } else if (strcmp(argv[i], "--vad_high") == 0) {
419 ASSERT_EQ(apm->kNoError, apm->voice_detection()->Enable(true));
420 ASSERT_EQ(apm->kNoError,
421 apm->voice_detection()->set_likelihood(
422 VoiceDetection::kHighLikelihood));
424 } else if (strcmp(argv[i], "--vad_out_file") == 0) {
426 ASSERT_LT(i, argc) << "Specify filename after --vad_out_file";
427 vad_out_filename = argv[i];
429 } else if (strcmp(argv[i], "--noasm") == 0) {
430 WebRtc_GetCPUInfo = WebRtc_GetCPUInfoNoASM;
431 // We need to reinitialize here if components have already been enabled.
432 ASSERT_EQ(apm->kNoError, apm->Initialize());
434 } else if (strcmp(argv[i], "--add_delay") == 0) {
436 ASSERT_EQ(1, sscanf(argv[i], "%d", &extra_delay_ms));
438 } else if (strcmp(argv[i], "--delay") == 0) {
440 ASSERT_EQ(1, sscanf(argv[i], "%d", &override_delay_ms));
442 } else if (strcmp(argv[i], "--perf") == 0) {
445 } else if (strcmp(argv[i], "--quiet") == 0) {
449 } else if (strcmp(argv[i], "--no_progress") == 0) {
452 } else if (strcmp(argv[i], "--debug_file") == 0) {
454 ASSERT_LT(i, argc) << "Specify filename after --debug_file";
455 ASSERT_EQ(apm->kNoError, apm->StartDebugRecording(argv[i]));
457 FAIL() << "Unrecognized argument " << argv[i];
460 // If we're reading a protobuf file, ensure a simulation hasn't also
461 // been requested (which makes no sense...)
462 ASSERT_FALSE(pb_filename && simulating);
465 printf("Sample rate: %d Hz\n", sample_rate_hz);
466 printf("Primary channels: %d (in), %d (out)\n",
467 num_capture_input_channels,
468 num_capture_output_channels);
469 printf("Reverse channels: %d \n", num_render_channels);
472 const std::string out_path = webrtc::test::OutputPath();
473 const char far_file_default[] = "apm_far.pcm";
474 const char near_file_default[] = "apm_near.pcm";
475 const std::string out_file_default = out_path + "out.pcm";
476 const char event_filename[] = "apm_event.dat";
477 const char delay_filename[] = "apm_delay.dat";
478 const char drift_filename[] = "apm_drift.dat";
479 const std::string vad_file_default = out_path + "vad_out.dat";
480 const std::string ns_prob_file_default = out_path + "ns_prob.dat";
483 far_filename = far_file_default;
484 near_filename = near_file_default;
488 out_filename = out_file_default.c_str();
491 if (!vad_out_filename) {
492 vad_out_filename = vad_file_default.c_str();
495 if (!ns_prob_filename) {
496 ns_prob_filename = ns_prob_file_default.c_str();
499 FILE* pb_file = NULL;
500 FILE* far_file = NULL;
501 FILE* near_file = NULL;
502 FILE* out_file = NULL;
503 FILE* event_file = NULL;
504 FILE* delay_file = NULL;
505 FILE* drift_file = NULL;
506 FILE* vad_out_file = NULL;
507 FILE* ns_prob_file = NULL;
508 FILE* aecm_echo_path_in_file = NULL;
509 FILE* aecm_echo_path_out_file = NULL;
512 pb_file = fopen(pb_filename, "rb");
513 ASSERT_TRUE(NULL != pb_file) << "Unable to open protobuf file "
517 far_file = fopen(far_filename, "rb");
518 ASSERT_TRUE(NULL != far_file) << "Unable to open far-end audio file "
522 near_file = fopen(near_filename, "rb");
523 ASSERT_TRUE(NULL != near_file) << "Unable to open near-end audio file "
526 event_file = fopen(event_filename, "rb");
527 ASSERT_TRUE(NULL != event_file) << "Unable to open event file "
530 delay_file = fopen(delay_filename, "rb");
531 ASSERT_TRUE(NULL != delay_file) << "Unable to open buffer file "
534 drift_file = fopen(drift_filename, "rb");
535 ASSERT_TRUE(NULL != drift_file) << "Unable to open drift file "
540 out_file = fopen(out_filename, "wb");
541 ASSERT_TRUE(NULL != out_file) << "Unable to open output audio file "
544 int near_size_bytes = 0;
547 stat(pb_filename, &st);
548 // Crude estimate, but should be good enough.
549 near_size_bytes = st.st_size / 3;
552 stat(near_filename, &st);
553 near_size_bytes = st.st_size;
556 if (apm->voice_detection()->is_enabled()) {
557 vad_out_file = fopen(vad_out_filename, "wb");
558 ASSERT_TRUE(NULL != vad_out_file) << "Unable to open VAD output file "
562 if (apm->noise_suppression()->is_enabled()) {
563 ns_prob_file = fopen(ns_prob_filename, "wb");
564 ASSERT_TRUE(NULL != ns_prob_file) << "Unable to open NS output file "
568 if (aecm_echo_path_in_filename != NULL) {
569 aecm_echo_path_in_file = fopen(aecm_echo_path_in_filename, "rb");
570 ASSERT_TRUE(NULL != aecm_echo_path_in_file) << "Unable to open file "
571 << aecm_echo_path_in_filename;
573 const size_t path_size =
574 apm->echo_control_mobile()->echo_path_size_bytes();
575 scoped_array<char> echo_path(new char[path_size]);
576 ASSERT_EQ(path_size, fread(echo_path.get(),
579 aecm_echo_path_in_file));
580 EXPECT_EQ(apm->kNoError,
581 apm->echo_control_mobile()->SetEchoPath(echo_path.get(),
583 fclose(aecm_echo_path_in_file);
584 aecm_echo_path_in_file = NULL;
587 if (aecm_echo_path_out_filename != NULL) {
588 aecm_echo_path_out_file = fopen(aecm_echo_path_out_filename, "wb");
589 ASSERT_TRUE(NULL != aecm_echo_path_out_file) << "Unable to open file "
590 << aecm_echo_path_out_filename;
593 size_t read_count = 0;
594 int reverse_count = 0;
595 int primary_count = 0;
596 int near_read_bytes = 0;
597 TickInterval acc_ticks;
599 AudioFrame far_frame;
600 AudioFrame near_frame;
603 int drift_samples = 0;
604 int capture_level = 127;
605 int8_t stream_has_voice = 0;
606 float ns_speech_prob = 0.0f;
608 TickTime t0 = TickTime::Now();
610 int64_t max_time_us = 0;
611 int64_t max_time_reverse_us = 0;
612 int64_t min_time_us = 1e6;
613 int64_t min_time_reverse_us = 1e6;
615 // TODO(ajm): Ideally we would refactor this block into separate functions,
616 // but for now we want to share the variables.
619 while (ReadMessageFromFile(pb_file, &event_msg)) {
620 std::ostringstream trace_stream;
621 trace_stream << "Processed frames: " << reverse_count << " (reverse), "
622 << primary_count << " (primary)";
623 SCOPED_TRACE(trace_stream.str());
625 if (event_msg.type() == Event::INIT) {
626 ASSERT_TRUE(event_msg.has_init());
627 const Init msg = event_msg.init();
629 ASSERT_TRUE(msg.has_sample_rate());
630 // TODO(bjornv): Replace set_sample_rate_hz() when we have a smarter
631 // AnalyzeReverseStream().
632 ASSERT_EQ(apm->kNoError, apm->set_sample_rate_hz(msg.sample_rate()));
633 ASSERT_TRUE(msg.has_device_sample_rate());
634 ASSERT_EQ(apm->kNoError,
635 apm->echo_cancellation()->set_device_sample_rate_hz(
636 msg.device_sample_rate()));
638 ASSERT_TRUE(msg.has_num_input_channels());
639 ASSERT_TRUE(msg.has_num_output_channels());
640 ASSERT_TRUE(msg.has_num_reverse_channels());
642 samples_per_channel = msg.sample_rate() / 100;
643 far_frame.sample_rate_hz_ = msg.sample_rate();
644 far_frame.samples_per_channel_ = samples_per_channel;
645 far_frame.num_channels_ = msg.num_reverse_channels();
646 near_frame.sample_rate_hz_ = msg.sample_rate();
647 near_frame.samples_per_channel_ = samples_per_channel;
648 near_frame.num_channels_ = msg.num_input_channels();
651 printf("Init at frame: %d (primary), %d (reverse)\n",
652 primary_count, reverse_count);
653 printf(" Sample rate: %d Hz\n", msg.sample_rate());
654 printf(" Primary channels: %d (in), %d (out)\n",
655 msg.num_input_channels(),
656 msg.num_output_channels());
657 printf(" Reverse channels: %d \n", msg.num_reverse_channels());
660 } else if (event_msg.type() == Event::REVERSE_STREAM) {
661 ASSERT_TRUE(event_msg.has_reverse_stream());
662 const ReverseStream msg = event_msg.reverse_stream();
665 ASSERT_TRUE(msg.has_data());
666 ASSERT_EQ(sizeof(int16_t) * samples_per_channel *
667 far_frame.num_channels_, msg.data().size());
668 memcpy(far_frame.data_, msg.data().data(), msg.data().size());
671 t0 = TickTime::Now();
674 ASSERT_EQ(apm->kNoError,
675 apm->AnalyzeReverseStream(&far_frame));
678 t1 = TickTime::Now();
679 TickInterval tick_diff = t1 - t0;
680 acc_ticks += tick_diff;
681 if (tick_diff.Microseconds() > max_time_reverse_us) {
682 max_time_reverse_us = tick_diff.Microseconds();
684 if (tick_diff.Microseconds() < min_time_reverse_us) {
685 min_time_reverse_us = tick_diff.Microseconds();
689 } else if (event_msg.type() == Event::STREAM) {
690 ASSERT_TRUE(event_msg.has_stream());
691 const Stream msg = event_msg.stream();
694 // ProcessStream could have changed this for the output frame.
695 near_frame.num_channels_ = apm->num_input_channels();
697 ASSERT_TRUE(msg.has_input_data());
698 ASSERT_EQ(sizeof(int16_t) * samples_per_channel *
699 near_frame.num_channels_, msg.input_data().size());
700 memcpy(near_frame.data_,
701 msg.input_data().data(),
702 msg.input_data().size());
704 near_read_bytes += msg.input_data().size();
705 if (progress && primary_count % 100 == 0) {
706 printf("%.0f%% complete\r",
707 (near_read_bytes * 100.0) / near_size_bytes);
712 t0 = TickTime::Now();
715 ASSERT_EQ(apm->kNoError,
716 apm->gain_control()->set_stream_analog_level(msg.level()));
717 delay_ms = msg.delay() + extra_delay_ms;
718 if (override_delay_ms) {
719 delay_ms = override_delay_ms;
721 ASSERT_EQ(apm->kNoError,
722 apm->set_stream_delay_ms(delay_ms));
723 apm->echo_cancellation()->set_stream_drift_samples(msg.drift());
725 int err = apm->ProcessStream(&near_frame);
726 if (err == apm->kBadStreamParameterWarning) {
727 printf("Bad parameter warning. %s\n", trace_stream.str().c_str());
729 ASSERT_TRUE(err == apm->kNoError ||
730 err == apm->kBadStreamParameterWarning);
731 ASSERT_TRUE(near_frame.num_channels_ == apm->num_output_channels());
734 static_cast<int8_t>(apm->voice_detection()->stream_has_voice());
735 if (vad_out_file != NULL) {
736 ASSERT_EQ(1u, fwrite(&stream_has_voice,
737 sizeof(stream_has_voice),
742 if (ns_prob_file != NULL) {
743 ns_speech_prob = apm->noise_suppression()->speech_probability();
744 ASSERT_EQ(1u, fwrite(&ns_speech_prob,
745 sizeof(ns_speech_prob),
751 t1 = TickTime::Now();
752 TickInterval tick_diff = t1 - t0;
753 acc_ticks += tick_diff;
754 if (tick_diff.Microseconds() > max_time_us) {
755 max_time_us = tick_diff.Microseconds();
757 if (tick_diff.Microseconds() < min_time_us) {
758 min_time_us = tick_diff.Microseconds();
762 size_t size = samples_per_channel * near_frame.num_channels_;
763 ASSERT_EQ(size, fwrite(near_frame.data_,
770 ASSERT_TRUE(feof(pb_file));
777 kResetEventDeprecated
780 while (simulating || feof(event_file) == 0) {
781 std::ostringstream trace_stream;
782 trace_stream << "Processed frames: " << reverse_count << " (reverse), "
783 << primary_count << " (primary)";
784 SCOPED_TRACE(trace_stream.str());
787 if (far_file == NULL) {
788 event = kCaptureEvent;
790 if (event == kRenderEvent) {
791 event = kCaptureEvent;
793 event = kRenderEvent;
797 read_count = fread(&event, sizeof(event), 1, event_file);
798 if (read_count != 1) {
803 far_frame.sample_rate_hz_ = sample_rate_hz;
804 far_frame.samples_per_channel_ = samples_per_channel;
805 far_frame.num_channels_ = num_render_channels;
806 near_frame.sample_rate_hz_ = sample_rate_hz;
807 near_frame.samples_per_channel_ = samples_per_channel;
809 if (event == kInitializeEvent || event == kResetEventDeprecated) {
811 fread(&sample_rate_hz, sizeof(sample_rate_hz), 1, event_file));
812 samples_per_channel = sample_rate_hz / 100;
815 fread(&device_sample_rate_hz,
816 sizeof(device_sample_rate_hz),
820 // TODO(bjornv): Replace set_sample_rate_hz() when we have a smarter
821 // AnalyzeReverseStream().
822 ASSERT_EQ(apm->kNoError, apm->set_sample_rate_hz(sample_rate_hz));
824 ASSERT_EQ(apm->kNoError,
825 apm->echo_cancellation()->set_device_sample_rate_hz(
826 device_sample_rate_hz));
828 far_frame.sample_rate_hz_ = sample_rate_hz;
829 far_frame.samples_per_channel_ = samples_per_channel;
830 far_frame.num_channels_ = num_render_channels;
831 near_frame.sample_rate_hz_ = sample_rate_hz;
832 near_frame.samples_per_channel_ = samples_per_channel;
835 printf("Init at frame: %d (primary), %d (reverse)\n",
836 primary_count, reverse_count);
837 printf(" Sample rate: %d Hz\n", sample_rate_hz);
840 } else if (event == kRenderEvent) {
843 size_t size = samples_per_channel * num_render_channels;
844 read_count = fread(far_frame.data_,
850 if (read_count != size) {
851 // Read an equal amount from the near file to avoid errors due to
852 // not reaching end-of-file.
853 EXPECT_EQ(0, fseek(near_file, read_count * sizeof(int16_t),
855 break; // This is expected.
858 ASSERT_EQ(size, read_count);
862 t0 = TickTime::Now();
865 ASSERT_EQ(apm->kNoError,
866 apm->AnalyzeReverseStream(&far_frame));
869 t1 = TickTime::Now();
870 TickInterval tick_diff = t1 - t0;
871 acc_ticks += tick_diff;
872 if (tick_diff.Microseconds() > max_time_reverse_us) {
873 max_time_reverse_us = tick_diff.Microseconds();
875 if (tick_diff.Microseconds() < min_time_reverse_us) {
876 min_time_reverse_us = tick_diff.Microseconds();
880 } else if (event == kCaptureEvent) {
882 near_frame.num_channels_ = num_capture_input_channels;
884 size_t size = samples_per_channel * num_capture_input_channels;
885 read_count = fread(near_frame.data_,
890 near_read_bytes += read_count * sizeof(int16_t);
891 if (progress && primary_count % 100 == 0) {
892 printf("%.0f%% complete\r",
893 (near_read_bytes * 100.0) / near_size_bytes);
897 if (read_count != size) {
898 break; // This is expected.
904 ASSERT_EQ(size, read_count);
906 // TODO(ajm): sizeof(delay_ms) for current files?
908 fread(&delay_ms, 2, 1, delay_file));
910 fread(&drift_samples, sizeof(drift_samples), 1, drift_file));
913 if (apm->gain_control()->is_enabled() &&
914 apm->gain_control()->mode() == GainControl::kAdaptiveAnalog) {
915 SimulateMic(capture_level, &near_frame);
919 t0 = TickTime::Now();
922 const int capture_level_in = capture_level;
923 ASSERT_EQ(apm->kNoError,
924 apm->gain_control()->set_stream_analog_level(capture_level));
925 delay_ms += extra_delay_ms;
926 if (override_delay_ms) {
927 delay_ms = override_delay_ms;
929 ASSERT_EQ(apm->kNoError,
930 apm->set_stream_delay_ms(delay_ms));
931 apm->echo_cancellation()->set_stream_drift_samples(drift_samples);
933 int err = apm->ProcessStream(&near_frame);
934 if (err == apm->kBadStreamParameterWarning) {
935 printf("Bad parameter warning. %s\n", trace_stream.str().c_str());
937 ASSERT_TRUE(err == apm->kNoError ||
938 err == apm->kBadStreamParameterWarning);
939 ASSERT_TRUE(near_frame.num_channels_ == apm->num_output_channels());
941 capture_level = apm->gain_control()->stream_analog_level();
944 static_cast<int8_t>(apm->voice_detection()->stream_has_voice());
945 if (vad_out_file != NULL) {
946 ASSERT_EQ(1u, fwrite(&stream_has_voice,
947 sizeof(stream_has_voice),
952 if (ns_prob_file != NULL) {
953 ns_speech_prob = apm->noise_suppression()->speech_probability();
954 ASSERT_EQ(1u, fwrite(&ns_speech_prob,
955 sizeof(ns_speech_prob),
960 if (apm->gain_control()->mode() != GainControl::kAdaptiveAnalog) {
961 ASSERT_EQ(capture_level_in, capture_level);
965 t1 = TickTime::Now();
966 TickInterval tick_diff = t1 - t0;
967 acc_ticks += tick_diff;
968 if (tick_diff.Microseconds() > max_time_us) {
969 max_time_us = tick_diff.Microseconds();
971 if (tick_diff.Microseconds() < min_time_us) {
972 min_time_us = tick_diff.Microseconds();
976 size = samples_per_channel * near_frame.num_channels_;
977 ASSERT_EQ(size, fwrite(near_frame.data_,
983 FAIL() << "Event " << event << " is unrecognized";
987 printf("100%% complete\r");
989 if (aecm_echo_path_out_file != NULL) {
990 const size_t path_size =
991 apm->echo_control_mobile()->echo_path_size_bytes();
992 scoped_array<char> echo_path(new char[path_size]);
993 apm->echo_control_mobile()->GetEchoPath(echo_path.get(), path_size);
994 ASSERT_EQ(path_size, fwrite(echo_path.get(),
997 aecm_echo_path_out_file));
998 fclose(aecm_echo_path_out_file);
999 aecm_echo_path_out_file = NULL;
1003 printf("\nProcessed frames: %d (primary), %d (reverse)\n",
1004 primary_count, reverse_count);
1006 if (apm->level_estimator()->is_enabled()) {
1007 printf("\n--Level metrics--\n");
1008 printf("RMS: %d dBFS\n", -apm->level_estimator()->RMS());
1010 if (apm->echo_cancellation()->are_metrics_enabled()) {
1011 EchoCancellation::Metrics metrics;
1012 apm->echo_cancellation()->GetMetrics(&metrics);
1013 printf("\n--Echo metrics--\n");
1014 printf("(avg, max, min)\n");
1016 PrintStat(metrics.echo_return_loss);
1018 PrintStat(metrics.echo_return_loss_enhancement);
1020 PrintStat(metrics.a_nlp);
1022 if (apm->echo_cancellation()->is_delay_logging_enabled()) {
1025 apm->echo_cancellation()->GetDelayMetrics(&median, &std);
1026 printf("\n--Delay metrics--\n");
1027 printf("Median: %3d\n", median);
1028 printf("Standard deviation: %3d\n", std);
1035 read_count = fread(&temp_int8, sizeof(temp_int8), 1, far_file);
1036 EXPECT_NE(0, feof(far_file)) << "Far-end file not fully processed";
1039 read_count = fread(&temp_int8, sizeof(temp_int8), 1, near_file);
1040 EXPECT_NE(0, feof(near_file)) << "Near-end file not fully processed";
1043 read_count = fread(&temp_int8, sizeof(temp_int8), 1, event_file);
1044 EXPECT_NE(0, feof(event_file)) << "Event file not fully processed";
1045 read_count = fread(&temp_int8, sizeof(temp_int8), 1, delay_file);
1046 EXPECT_NE(0, feof(delay_file)) << "Delay file not fully processed";
1047 read_count = fread(&temp_int8, sizeof(temp_int8), 1, drift_file);
1048 EXPECT_NE(0, feof(drift_file)) << "Drift file not fully processed";
1053 if (primary_count > 0) {
1054 int64_t exec_time = acc_ticks.Milliseconds();
1055 printf("\nTotal time: %.3f s, file time: %.2f s\n",
1056 exec_time * 0.001, primary_count * 0.01);
1057 printf("Time per frame: %.3f ms (average), %.3f ms (max),"
1059 (exec_time * 1.0) / primary_count,
1060 (max_time_us + max_time_reverse_us) / 1000.0,
1061 (min_time_us + min_time_reverse_us) / 1000.0);
1062 // Record the results with Perf test tools.
1063 webrtc::test::PrintResult("audioproc", "", "time_per_10ms_frame",
1064 (exec_time * 1000) / primary_count, "us", false);
1066 printf("Warning: no capture frames\n");
1072 int main(int argc, char* argv[])
1074 void_main(argc, argv);
1076 // Optional, but removes memory leak noise from Valgrind.
1077 google::protobuf::ShutdownProtobufLibrary();