Upstream version 5.34.92.0
[platform/framework/web/crosswalk.git] / src / third_party / webrtc / modules / audio_processing / test / process_test.cc
1 /*
2  *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
3  *
4  *  Use of this source code is governed by a BSD-style license
5  *  that can be found in the LICENSE file in the root of the source
6  *  tree. An additional intellectual property rights grant can be found
7  *  in the file PATENTS.  All contributing project authors may
8  *  be found in the AUTHORS file in the root of the source tree.
9  */
10
11 #include <math.h>
12 #include <stdio.h>
13 #include <string.h>
14 #ifdef WEBRTC_ANDROID
15 #include <sys/stat.h>
16 #endif
17
18 #include <algorithm>
19
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"
31 #else
32 #include "testing/gtest/include/gtest/gtest.h"
33 #include "webrtc/audio_processing/debug.pb.h"
34 #endif
35
36 using webrtc::AudioFrame;
37 using webrtc::AudioProcessing;
38 using webrtc::Config;
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;
48
49 using webrtc::audioproc::Event;
50 using webrtc::audioproc::Init;
51 using webrtc::audioproc::ReverseStream;
52 using webrtc::audioproc::Stream;
53
54 namespace {
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.
60   int32_t size = 0;
61   if (fread(&size, sizeof(int32_t), 1, file) != 1) {
62     return false;
63   }
64   if (size <= 0) {
65     return false;
66   }
67   const size_t usize = static_cast<size_t>(size);
68
69   scoped_array<char> array(new char[usize]);
70   if (fread(array.get(), sizeof(char), usize, file) != usize) {
71     return false;
72   }
73
74   msg->Clear();
75   return msg->ParseFromArray(array.get(), usize);
76 }
77
78 void PrintStat(const AudioProcessing::Statistic& stat) {
79   printf("%d, %d, %d\n", stat.average,
80                          stat.maximum,
81                          stat.minimum);
82 }
83
84 void usage() {
85   printf(
86   "Usage: process_test [options] [-pb PROTOBUF_FILE]\n"
87   "  [-ir REVERSE_FILE] [-i PRIMARY_FILE] [-o OUT_FILE]\n");
88   printf(
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");
94   printf("\n");
95   printf("Options\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");
100   printf("\n");
101   printf("Component configuration:\n");
102   printf(
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");
137   printf("\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");
146 }
147
148 static float MicLevel2Gain(int level) {
149   return pow(10.0f, ((level - 127.0f) / 128.0f * 40.0f) / 20.0f);
150 }
151
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_;
156   float v;
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);
161   }
162 }
163
164 // void function for gtest.
165 void void_main(int argc, char* argv[]) {
166   if (argc > 1 && strcmp(argv[1], "--help") == 0) {
167     usage();
168     return;
169   }
170
171   if (argc < 2) {
172     printf("Did you mean to run without arguments?\n");
173     printf("Try `process_test --help' for more information.\n\n");
174   }
175
176   scoped_ptr<AudioProcessing> apm(AudioProcessing::Create(0));
177   ASSERT_TRUE(apm.get() != NULL);
178
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;
187
188   int32_t sample_rate_hz = 16000;
189   int32_t device_sample_rate_hz = 16000;
190
191   int num_capture_input_channels = 1;
192   int num_capture_output_channels = 1;
193   int num_render_channels = 1;
194
195   int samples_per_channel = sample_rate_hz / 100;
196
197   bool simulating = false;
198   bool perf_testing = false;
199   bool verbose = true;
200   bool progress = true;
201   int extra_delay_ms = 0;
202   int override_delay_ms = 0;
203   //bool interleaved = true;
204
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) {
208       i++;
209       ASSERT_LT(i, argc) << "Specify protobuf filename after -pb";
210       pb_filename = argv[i];
211
212     } else if (strcmp(argv[i], "-ir") == 0) {
213       i++;
214       ASSERT_LT(i, argc) << "Specify filename after -ir";
215       far_filename = argv[i];
216       simulating = true;
217
218     } else if (strcmp(argv[i], "-i") == 0) {
219       i++;
220       ASSERT_LT(i, argc) << "Specify filename after -i";
221       near_filename = argv[i];
222       simulating = true;
223
224     } else if (strcmp(argv[i], "-o") == 0) {
225       i++;
226       ASSERT_LT(i, argc) << "Specify filename after -o";
227       out_filename = argv[i];
228
229     } else if (strcmp(argv[i], "-fs") == 0) {
230       i++;
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;
234
235     } else if (strcmp(argv[i], "-ch") == 0) {
236       i++;
237       ASSERT_LT(i + 1, argc) << "Specify number of channels after -ch";
238       ASSERT_EQ(1, sscanf(argv[i], "%d", &num_capture_input_channels));
239       i++;
240       ASSERT_EQ(1, sscanf(argv[i], "%d", &num_capture_output_channels));
241
242     } else if (strcmp(argv[i], "-rch") == 0) {
243       i++;
244       ASSERT_LT(i, argc) << "Specify number of channels after -rch";
245       ASSERT_EQ(1, sscanf(argv[i], "%d", &num_render_channels));
246
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));
253
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));
264
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));
269
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));
274
275     } else if (strcmp(argv[i], "--no_level_metrics") == 0) {
276       ASSERT_EQ(apm->kNoError, apm->level_estimator()->Enable(false));
277
278     } else if (strcmp(argv[i], "--aec_suppression_level") == 0) {
279       i++;
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)));
287
288     } else if (strcmp(argv[i], "--extended_filter") == 0) {
289       Config config;
290       config.Set<DelayCorrection>(new DelayCorrection(true));
291       apm->SetExtraOptions(config);
292
293     } else if (strcmp(argv[i], "-aecm") == 0) {
294       ASSERT_EQ(apm->kNoError, apm->echo_control_mobile()->Enable(true));
295
296     } else if (strcmp(argv[i], "--aecm_echo_path_in_file") == 0) {
297       i++;
298       ASSERT_LT(i, argc) << "Specify filename after --aecm_echo_path_in_file";
299       aecm_echo_path_in_filename = argv[i];
300
301     } else if (strcmp(argv[i], "--aecm_echo_path_out_file") == 0) {
302       i++;
303       ASSERT_LT(i, argc) << "Specify filename after --aecm_echo_path_out_file";
304       aecm_echo_path_out_filename = argv[i];
305
306     } else if (strcmp(argv[i], "--no_comfort_noise") == 0) {
307       ASSERT_EQ(apm->kNoError,
308                 apm->echo_control_mobile()->enable_comfort_noise(false));
309
310     } else if (strcmp(argv[i], "--routing_mode") == 0) {
311       i++;
312       ASSERT_LT(i, argc) << "Specify mode after --routing_mode";
313       int 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>(
318                         routing_mode)));
319
320     } else if (strcmp(argv[i], "-agc") == 0) {
321       ASSERT_EQ(apm->kNoError, apm->gain_control()->Enable(true));
322
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));
327
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));
332
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));
337
338     } else if (strcmp(argv[i], "--target_level") == 0) {
339       i++;
340       int level;
341       ASSERT_EQ(1, sscanf(argv[i], "%d", &level));
342
343       ASSERT_EQ(apm->kNoError, apm->gain_control()->Enable(true));
344       ASSERT_EQ(apm->kNoError,
345                 apm->gain_control()->set_target_level_dbfs(level));
346
347     } else if (strcmp(argv[i], "--compression_gain") == 0) {
348       i++;
349       int gain;
350       ASSERT_EQ(1, sscanf(argv[i], "%d", &gain));
351
352       ASSERT_EQ(apm->kNoError, apm->gain_control()->Enable(true));
353       ASSERT_EQ(apm->kNoError,
354                 apm->gain_control()->set_compression_gain_db(gain));
355
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));
360
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));
365
366     } else if (strcmp(argv[i], "-hpf") == 0) {
367       ASSERT_EQ(apm->kNoError, apm->high_pass_filter()->Enable(true));
368
369     } else if (strcmp(argv[i], "-ns") == 0) {
370       ASSERT_EQ(apm->kNoError, apm->noise_suppression()->Enable(true));
371
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));
376
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));
381
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));
386
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));
391
392     } else if (strcmp(argv[i], "--ns_prob_file") == 0) {
393       i++;
394       ASSERT_LT(i, argc) << "Specify filename after --ns_prob_file";
395       ns_prob_filename = argv[i];
396
397     } else if (strcmp(argv[i], "-vad") == 0) {
398       ASSERT_EQ(apm->kNoError, apm->voice_detection()->Enable(true));
399
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));
405
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));
411
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));
417
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));
423
424     } else if (strcmp(argv[i], "--vad_out_file") == 0) {
425       i++;
426       ASSERT_LT(i, argc) << "Specify filename after --vad_out_file";
427       vad_out_filename = argv[i];
428
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());
433
434     } else if (strcmp(argv[i], "--add_delay") == 0) {
435       i++;
436       ASSERT_EQ(1, sscanf(argv[i], "%d", &extra_delay_ms));
437
438     } else if (strcmp(argv[i], "--delay") == 0) {
439       i++;
440       ASSERT_EQ(1, sscanf(argv[i], "%d", &override_delay_ms));
441
442     } else if (strcmp(argv[i], "--perf") == 0) {
443       perf_testing = true;
444
445     } else if (strcmp(argv[i], "--quiet") == 0) {
446       verbose = false;
447       progress = false;
448
449     } else if (strcmp(argv[i], "--no_progress") == 0) {
450       progress = false;
451
452     } else if (strcmp(argv[i], "--debug_file") == 0) {
453       i++;
454       ASSERT_LT(i, argc) << "Specify filename after --debug_file";
455       ASSERT_EQ(apm->kNoError, apm->StartDebugRecording(argv[i]));
456     } else {
457       FAIL() << "Unrecognized argument " << argv[i];
458     }
459   }
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);
463
464   if (verbose) {
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);
470   }
471
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";
481
482   if (!simulating) {
483     far_filename = far_file_default;
484     near_filename = near_file_default;
485   }
486
487   if (!out_filename) {
488     out_filename = out_file_default.c_str();
489   }
490
491   if (!vad_out_filename) {
492     vad_out_filename = vad_file_default.c_str();
493   }
494
495   if (!ns_prob_filename) {
496     ns_prob_filename = ns_prob_file_default.c_str();
497   }
498
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;
510
511   if (pb_filename) {
512     pb_file = fopen(pb_filename, "rb");
513     ASSERT_TRUE(NULL != pb_file) << "Unable to open protobuf file "
514                                  << pb_filename;
515   } else {
516     if (far_filename) {
517       far_file = fopen(far_filename, "rb");
518       ASSERT_TRUE(NULL != far_file) << "Unable to open far-end audio file "
519                                     << far_filename;
520     }
521
522     near_file = fopen(near_filename, "rb");
523     ASSERT_TRUE(NULL != near_file) << "Unable to open near-end audio file "
524                                    << near_filename;
525     if (!simulating) {
526       event_file = fopen(event_filename, "rb");
527       ASSERT_TRUE(NULL != event_file) << "Unable to open event file "
528                                       << event_filename;
529
530       delay_file = fopen(delay_filename, "rb");
531       ASSERT_TRUE(NULL != delay_file) << "Unable to open buffer file "
532                                       << delay_filename;
533
534       drift_file = fopen(drift_filename, "rb");
535       ASSERT_TRUE(NULL != drift_file) << "Unable to open drift file "
536                                       << drift_filename;
537     }
538   }
539
540   out_file = fopen(out_filename, "wb");
541   ASSERT_TRUE(NULL != out_file) << "Unable to open output audio file "
542                                 << out_filename;
543
544   int near_size_bytes = 0;
545   if (pb_file) {
546     struct stat st;
547     stat(pb_filename, &st);
548     // Crude estimate, but should be good enough.
549     near_size_bytes = st.st_size / 3;
550   } else {
551     struct stat st;
552     stat(near_filename, &st);
553     near_size_bytes = st.st_size;
554   }
555
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 "
559                                       << vad_out_file;
560   }
561
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 "
565                                       << ns_prob_file;
566   }
567
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;
572
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(),
577                                sizeof(char),
578                                path_size,
579                                aecm_echo_path_in_file));
580     EXPECT_EQ(apm->kNoError,
581               apm->echo_control_mobile()->SetEchoPath(echo_path.get(),
582                                                       path_size));
583     fclose(aecm_echo_path_in_file);
584     aecm_echo_path_in_file = NULL;
585   }
586
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;
591   }
592
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;
598
599   AudioFrame far_frame;
600   AudioFrame near_frame;
601
602   int delay_ms = 0;
603   int drift_samples = 0;
604   int capture_level = 127;
605   int8_t stream_has_voice = 0;
606   float ns_speech_prob = 0.0f;
607
608   TickTime t0 = TickTime::Now();
609   TickTime t1 = t0;
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;
614
615   // TODO(ajm): Ideally we would refactor this block into separate functions,
616   //            but for now we want to share the variables.
617   if (pb_file) {
618     Event event_msg;
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());
624
625       if (event_msg.type() == Event::INIT) {
626         ASSERT_TRUE(event_msg.has_init());
627         const Init msg = event_msg.init();
628
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()));
637
638         ASSERT_TRUE(msg.has_num_input_channels());
639         ASSERT_TRUE(msg.has_num_output_channels());
640         ASSERT_TRUE(msg.has_num_reverse_channels());
641
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();
649
650         if (verbose) {
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());
658         }
659
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();
663         reverse_count++;
664
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());
669
670         if (perf_testing) {
671           t0 = TickTime::Now();
672         }
673
674         ASSERT_EQ(apm->kNoError,
675                   apm->AnalyzeReverseStream(&far_frame));
676
677         if (perf_testing) {
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();
683           }
684           if (tick_diff.Microseconds() < min_time_reverse_us) {
685             min_time_reverse_us = tick_diff.Microseconds();
686           }
687         }
688
689       } else if (event_msg.type() == Event::STREAM) {
690         ASSERT_TRUE(event_msg.has_stream());
691         const Stream msg = event_msg.stream();
692         primary_count++;
693
694         // ProcessStream could have changed this for the output frame.
695         near_frame.num_channels_ = apm->num_input_channels();
696
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());
703
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);
708           fflush(stdout);
709         }
710
711         if (perf_testing) {
712           t0 = TickTime::Now();
713         }
714
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;
720         }
721         ASSERT_EQ(apm->kNoError,
722                   apm->set_stream_delay_ms(delay_ms));
723         apm->echo_cancellation()->set_stream_drift_samples(msg.drift());
724
725         int err = apm->ProcessStream(&near_frame);
726         if (err == apm->kBadStreamParameterWarning) {
727           printf("Bad parameter warning. %s\n", trace_stream.str().c_str());
728         }
729         ASSERT_TRUE(err == apm->kNoError ||
730                     err == apm->kBadStreamParameterWarning);
731         ASSERT_TRUE(near_frame.num_channels_ == apm->num_output_channels());
732
733         stream_has_voice =
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),
738                                1,
739                                vad_out_file));
740         }
741
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),
746                                1,
747                                ns_prob_file));
748         }
749
750         if (perf_testing) {
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();
756           }
757           if (tick_diff.Microseconds() < min_time_us) {
758             min_time_us = tick_diff.Microseconds();
759           }
760         }
761
762         size_t size = samples_per_channel * near_frame.num_channels_;
763         ASSERT_EQ(size, fwrite(near_frame.data_,
764                                sizeof(int16_t),
765                                size,
766                                out_file));
767       }
768     }
769
770     ASSERT_TRUE(feof(pb_file));
771
772   } else {
773     enum Events {
774       kInitializeEvent,
775       kRenderEvent,
776       kCaptureEvent,
777       kResetEventDeprecated
778     };
779     int16_t event = 0;
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());
785
786       if (simulating) {
787         if (far_file == NULL) {
788           event = kCaptureEvent;
789         } else {
790           if (event == kRenderEvent) {
791             event = kCaptureEvent;
792           } else {
793             event = kRenderEvent;
794           }
795         }
796       } else {
797         read_count = fread(&event, sizeof(event), 1, event_file);
798         if (read_count != 1) {
799           break;
800         }
801       }
802
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;
808
809       if (event == kInitializeEvent || event == kResetEventDeprecated) {
810         ASSERT_EQ(1u,
811             fread(&sample_rate_hz, sizeof(sample_rate_hz), 1, event_file));
812         samples_per_channel = sample_rate_hz / 100;
813
814         ASSERT_EQ(1u,
815             fread(&device_sample_rate_hz,
816                   sizeof(device_sample_rate_hz),
817                   1,
818                   event_file));
819
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));
823
824         ASSERT_EQ(apm->kNoError,
825                   apm->echo_cancellation()->set_device_sample_rate_hz(
826                       device_sample_rate_hz));
827
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;
833
834         if (verbose) {
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);
838         }
839
840       } else if (event == kRenderEvent) {
841         reverse_count++;
842
843         size_t size = samples_per_channel * num_render_channels;
844         read_count = fread(far_frame.data_,
845                            sizeof(int16_t),
846                            size,
847                            far_file);
848
849         if (simulating) {
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),
854                       SEEK_CUR));
855             break; // This is expected.
856           }
857         } else {
858           ASSERT_EQ(size, read_count);
859         }
860
861         if (perf_testing) {
862           t0 = TickTime::Now();
863         }
864
865         ASSERT_EQ(apm->kNoError,
866                   apm->AnalyzeReverseStream(&far_frame));
867
868         if (perf_testing) {
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();
874           }
875           if (tick_diff.Microseconds() < min_time_reverse_us) {
876             min_time_reverse_us = tick_diff.Microseconds();
877           }
878         }
879
880       } else if (event == kCaptureEvent) {
881         primary_count++;
882         near_frame.num_channels_ = num_capture_input_channels;
883
884         size_t size = samples_per_channel * num_capture_input_channels;
885         read_count = fread(near_frame.data_,
886                            sizeof(int16_t),
887                            size,
888                            near_file);
889
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);
894           fflush(stdout);
895         }
896         if (simulating) {
897           if (read_count != size) {
898             break; // This is expected.
899           }
900
901           delay_ms = 0;
902           drift_samples = 0;
903         } else {
904           ASSERT_EQ(size, read_count);
905
906           // TODO(ajm): sizeof(delay_ms) for current files?
907           ASSERT_EQ(1u,
908               fread(&delay_ms, 2, 1, delay_file));
909           ASSERT_EQ(1u,
910               fread(&drift_samples, sizeof(drift_samples), 1, drift_file));
911         }
912
913         if (apm->gain_control()->is_enabled() &&
914             apm->gain_control()->mode() == GainControl::kAdaptiveAnalog) {
915           SimulateMic(capture_level, &near_frame);
916         }
917
918         if (perf_testing) {
919           t0 = TickTime::Now();
920         }
921
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;
928         }
929         ASSERT_EQ(apm->kNoError,
930                   apm->set_stream_delay_ms(delay_ms));
931         apm->echo_cancellation()->set_stream_drift_samples(drift_samples);
932
933         int err = apm->ProcessStream(&near_frame);
934         if (err == apm->kBadStreamParameterWarning) {
935           printf("Bad parameter warning. %s\n", trace_stream.str().c_str());
936         }
937         ASSERT_TRUE(err == apm->kNoError ||
938                     err == apm->kBadStreamParameterWarning);
939         ASSERT_TRUE(near_frame.num_channels_ == apm->num_output_channels());
940
941         capture_level = apm->gain_control()->stream_analog_level();
942
943         stream_has_voice =
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),
948                                1,
949                                vad_out_file));
950         }
951
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),
956                                1,
957                                ns_prob_file));
958         }
959
960         if (apm->gain_control()->mode() != GainControl::kAdaptiveAnalog) {
961           ASSERT_EQ(capture_level_in, capture_level);
962         }
963
964         if (perf_testing) {
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();
970           }
971           if (tick_diff.Microseconds() < min_time_us) {
972             min_time_us = tick_diff.Microseconds();
973           }
974         }
975
976         size = samples_per_channel * near_frame.num_channels_;
977         ASSERT_EQ(size, fwrite(near_frame.data_,
978                                sizeof(int16_t),
979                                size,
980                                out_file));
981       }
982       else {
983         FAIL() << "Event " << event << " is unrecognized";
984       }
985     }
986   }
987   printf("100%% complete\r");
988
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(),
995                                 sizeof(char),
996                                 path_size,
997                                 aecm_echo_path_out_file));
998     fclose(aecm_echo_path_out_file);
999     aecm_echo_path_out_file = NULL;
1000   }
1001
1002   if (verbose) {
1003     printf("\nProcessed frames: %d (primary), %d (reverse)\n",
1004         primary_count, reverse_count);
1005
1006     if (apm->level_estimator()->is_enabled()) {
1007       printf("\n--Level metrics--\n");
1008       printf("RMS: %d dBFS\n", -apm->level_estimator()->RMS());
1009     }
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");
1015       printf("ERL:  ");
1016       PrintStat(metrics.echo_return_loss);
1017       printf("ERLE: ");
1018       PrintStat(metrics.echo_return_loss_enhancement);
1019       printf("ANLP: ");
1020       PrintStat(metrics.a_nlp);
1021     }
1022     if (apm->echo_cancellation()->is_delay_logging_enabled()) {
1023       int median = 0;
1024       int std = 0;
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);
1029     }
1030   }
1031
1032   if (!pb_file) {
1033     int8_t temp_int8;
1034     if (far_file) {
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";
1037     }
1038
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";
1041
1042     if (!simulating) {
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";
1049     }
1050   }
1051
1052   if (perf_testing) {
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),"
1058              " %.3f ms (min)\n",
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);
1065     } else {
1066       printf("Warning: no capture frames\n");
1067     }
1068   }
1069 }
1070 }  // namespace
1071
1072 int main(int argc, char* argv[])
1073 {
1074   void_main(argc, argv);
1075
1076   // Optional, but removes memory leak noise from Valgrind.
1077   google::protobuf::ShutdownProtobufLibrary();
1078   return 0;
1079 }