- add third_party src.
[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       ASSERT_EQ(apm->kNoError,
236                 apm->set_sample_rate_hz(sample_rate_hz));
237
238     } else if (strcmp(argv[i], "-ch") == 0) {
239       i++;
240       ASSERT_LT(i + 1, argc) << "Specify number of channels after -ch";
241       ASSERT_EQ(1, sscanf(argv[i], "%d", &num_capture_input_channels));
242       i++;
243       ASSERT_EQ(1, sscanf(argv[i], "%d", &num_capture_output_channels));
244
245       ASSERT_EQ(apm->kNoError,
246                 apm->set_num_channels(num_capture_input_channels,
247                                       num_capture_output_channels));
248
249     } else if (strcmp(argv[i], "-rch") == 0) {
250       i++;
251       ASSERT_LT(i, argc) << "Specify number of channels after -rch";
252       ASSERT_EQ(1, sscanf(argv[i], "%d", &num_render_channels));
253
254       ASSERT_EQ(apm->kNoError,
255                 apm->set_num_reverse_channels(num_render_channels));
256
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));
263
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));
274
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));
279
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));
284
285     } else if (strcmp(argv[i], "--no_level_metrics") == 0) {
286       ASSERT_EQ(apm->kNoError, apm->level_estimator()->Enable(false));
287
288     } else if (strcmp(argv[i], "--aec_suppression_level") == 0) {
289       i++;
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)));
297
298     } else if (strcmp(argv[i], "--extended_filter") == 0) {
299       Config config;
300       config.Set<DelayCorrection>(new DelayCorrection(true));
301       apm->SetExtraOptions(config);
302
303     } else if (strcmp(argv[i], "-aecm") == 0) {
304       ASSERT_EQ(apm->kNoError, apm->echo_control_mobile()->Enable(true));
305
306     } else if (strcmp(argv[i], "--aecm_echo_path_in_file") == 0) {
307       i++;
308       ASSERT_LT(i, argc) << "Specify filename after --aecm_echo_path_in_file";
309       aecm_echo_path_in_filename = argv[i];
310
311     } else if (strcmp(argv[i], "--aecm_echo_path_out_file") == 0) {
312       i++;
313       ASSERT_LT(i, argc) << "Specify filename after --aecm_echo_path_out_file";
314       aecm_echo_path_out_filename = argv[i];
315
316     } else if (strcmp(argv[i], "--no_comfort_noise") == 0) {
317       ASSERT_EQ(apm->kNoError,
318                 apm->echo_control_mobile()->enable_comfort_noise(false));
319
320     } else if (strcmp(argv[i], "--routing_mode") == 0) {
321       i++;
322       ASSERT_LT(i, argc) << "Specify mode after --routing_mode";
323       int 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>(
328                         routing_mode)));
329
330     } else if (strcmp(argv[i], "-agc") == 0) {
331       ASSERT_EQ(apm->kNoError, apm->gain_control()->Enable(true));
332
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));
337
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));
342
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));
347
348     } else if (strcmp(argv[i], "--target_level") == 0) {
349       i++;
350       int level;
351       ASSERT_EQ(1, sscanf(argv[i], "%d", &level));
352
353       ASSERT_EQ(apm->kNoError, apm->gain_control()->Enable(true));
354       ASSERT_EQ(apm->kNoError,
355                 apm->gain_control()->set_target_level_dbfs(level));
356
357     } else if (strcmp(argv[i], "--compression_gain") == 0) {
358       i++;
359       int gain;
360       ASSERT_EQ(1, sscanf(argv[i], "%d", &gain));
361
362       ASSERT_EQ(apm->kNoError, apm->gain_control()->Enable(true));
363       ASSERT_EQ(apm->kNoError,
364                 apm->gain_control()->set_compression_gain_db(gain));
365
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));
370
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));
375
376     } else if (strcmp(argv[i], "-hpf") == 0) {
377       ASSERT_EQ(apm->kNoError, apm->high_pass_filter()->Enable(true));
378
379     } else if (strcmp(argv[i], "-ns") == 0) {
380       ASSERT_EQ(apm->kNoError, apm->noise_suppression()->Enable(true));
381
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));
386
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));
391
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));
396
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));
401
402     } else if (strcmp(argv[i], "--ns_prob_file") == 0) {
403       i++;
404       ASSERT_LT(i, argc) << "Specify filename after --ns_prob_file";
405       ns_prob_filename = argv[i];
406
407     } else if (strcmp(argv[i], "-vad") == 0) {
408       ASSERT_EQ(apm->kNoError, apm->voice_detection()->Enable(true));
409
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));
415
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));
421
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));
427
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));
433
434     } else if (strcmp(argv[i], "--vad_out_file") == 0) {
435       i++;
436       ASSERT_LT(i, argc) << "Specify filename after --vad_out_file";
437       vad_out_filename = argv[i];
438
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());
443
444     } else if (strcmp(argv[i], "--add_delay") == 0) {
445       i++;
446       ASSERT_EQ(1, sscanf(argv[i], "%d", &extra_delay_ms));
447
448     } else if (strcmp(argv[i], "--delay") == 0) {
449       i++;
450       ASSERT_EQ(1, sscanf(argv[i], "%d", &override_delay_ms));
451
452     } else if (strcmp(argv[i], "--perf") == 0) {
453       perf_testing = true;
454
455     } else if (strcmp(argv[i], "--quiet") == 0) {
456       verbose = false;
457       progress = false;
458
459     } else if (strcmp(argv[i], "--no_progress") == 0) {
460       progress = false;
461
462     } else if (strcmp(argv[i], "--debug_file") == 0) {
463       i++;
464       ASSERT_LT(i, argc) << "Specify filename after --debug_file";
465       ASSERT_EQ(apm->kNoError, apm->StartDebugRecording(argv[i]));
466     } else {
467       FAIL() << "Unrecognized argument " << argv[i];
468     }
469   }
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);
473
474   if (verbose) {
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);
480   }
481
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";
491
492   if (!simulating) {
493     far_filename = far_file_default;
494     near_filename = near_file_default;
495   }
496
497   if (!out_filename) {
498     out_filename = out_file_default.c_str();
499   }
500
501   if (!vad_out_filename) {
502     vad_out_filename = vad_file_default.c_str();
503   }
504
505   if (!ns_prob_filename) {
506     ns_prob_filename = ns_prob_file_default.c_str();
507   }
508
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;
520
521   if (pb_filename) {
522     pb_file = fopen(pb_filename, "rb");
523     ASSERT_TRUE(NULL != pb_file) << "Unable to open protobuf file "
524                                  << pb_filename;
525   } else {
526     if (far_filename) {
527       far_file = fopen(far_filename, "rb");
528       ASSERT_TRUE(NULL != far_file) << "Unable to open far-end audio file "
529                                     << far_filename;
530     }
531
532     near_file = fopen(near_filename, "rb");
533     ASSERT_TRUE(NULL != near_file) << "Unable to open near-end audio file "
534                                    << near_filename;
535     if (!simulating) {
536       event_file = fopen(event_filename, "rb");
537       ASSERT_TRUE(NULL != event_file) << "Unable to open event file "
538                                       << event_filename;
539
540       delay_file = fopen(delay_filename, "rb");
541       ASSERT_TRUE(NULL != delay_file) << "Unable to open buffer file "
542                                       << delay_filename;
543
544       drift_file = fopen(drift_filename, "rb");
545       ASSERT_TRUE(NULL != drift_file) << "Unable to open drift file "
546                                       << drift_filename;
547     }
548   }
549
550   out_file = fopen(out_filename, "wb");
551   ASSERT_TRUE(NULL != out_file) << "Unable to open output audio file "
552                                 << out_filename;
553
554   int near_size_bytes = 0;
555   if (pb_file) {
556     struct stat st;
557     stat(pb_filename, &st);
558     // Crude estimate, but should be good enough.
559     near_size_bytes = st.st_size / 3;
560   } else {
561     struct stat st;
562     stat(near_filename, &st);
563     near_size_bytes = st.st_size;
564   }
565
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 "
569                                       << vad_out_file;
570   }
571
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 "
575                                       << ns_prob_file;
576   }
577
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;
582
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(),
587                                sizeof(char),
588                                path_size,
589                                aecm_echo_path_in_file));
590     EXPECT_EQ(apm->kNoError,
591               apm->echo_control_mobile()->SetEchoPath(echo_path.get(),
592                                                       path_size));
593     fclose(aecm_echo_path_in_file);
594     aecm_echo_path_in_file = NULL;
595   }
596
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;
601   }
602
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;
608
609   AudioFrame far_frame;
610   AudioFrame near_frame;
611
612   int delay_ms = 0;
613   int drift_samples = 0;
614   int capture_level = 127;
615   int8_t stream_has_voice = 0;
616   float ns_speech_prob = 0.0f;
617
618   TickTime t0 = TickTime::Now();
619   TickTime t1 = t0;
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;
624
625   // TODO(ajm): Ideally we would refactor this block into separate functions,
626   //            but for now we want to share the variables.
627   if (pb_file) {
628     Event event_msg;
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());
634
635       if (event_msg.type() == Event::INIT) {
636         ASSERT_TRUE(event_msg.has_init());
637         const Init msg = event_msg.init();
638
639         ASSERT_TRUE(msg.has_sample_rate());
640         ASSERT_EQ(apm->kNoError,
641             apm->set_sample_rate_hz(msg.sample_rate()));
642
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()));
647
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()));
653
654         ASSERT_TRUE(msg.has_num_reverse_channels());
655         ASSERT_EQ(apm->kNoError,
656             apm->set_num_reverse_channels(msg.num_reverse_channels()));
657
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();
665
666         if (verbose) {
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());
674         }
675
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();
679         reverse_count++;
680
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());
685
686         if (perf_testing) {
687           t0 = TickTime::Now();
688         }
689
690         ASSERT_EQ(apm->kNoError,
691                   apm->AnalyzeReverseStream(&far_frame));
692
693         if (perf_testing) {
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();
699           }
700           if (tick_diff.Microseconds() < min_time_reverse_us) {
701             min_time_reverse_us = tick_diff.Microseconds();
702           }
703         }
704
705       } else if (event_msg.type() == Event::STREAM) {
706         ASSERT_TRUE(event_msg.has_stream());
707         const Stream msg = event_msg.stream();
708         primary_count++;
709
710         // ProcessStream could have changed this for the output frame.
711         near_frame.num_channels_ = apm->num_input_channels();
712
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());
719
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);
724           fflush(stdout);
725         }
726
727         if (perf_testing) {
728           t0 = TickTime::Now();
729         }
730
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;
736         }
737         ASSERT_EQ(apm->kNoError,
738                   apm->set_stream_delay_ms(delay_ms));
739         apm->echo_cancellation()->set_stream_drift_samples(msg.drift());
740
741         int err = apm->ProcessStream(&near_frame);
742         if (err == apm->kBadStreamParameterWarning) {
743           printf("Bad parameter warning. %s\n", trace_stream.str().c_str());
744         }
745         ASSERT_TRUE(err == apm->kNoError ||
746                     err == apm->kBadStreamParameterWarning);
747         ASSERT_TRUE(near_frame.num_channels_ == apm->num_output_channels());
748
749         stream_has_voice =
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),
754                                1,
755                                vad_out_file));
756         }
757
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),
762                                1,
763                                ns_prob_file));
764         }
765
766         if (perf_testing) {
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();
772           }
773           if (tick_diff.Microseconds() < min_time_us) {
774             min_time_us = tick_diff.Microseconds();
775           }
776         }
777
778         size_t size = samples_per_channel * near_frame.num_channels_;
779         ASSERT_EQ(size, fwrite(near_frame.data_,
780                                sizeof(int16_t),
781                                size,
782                                out_file));
783       }
784     }
785
786     ASSERT_TRUE(feof(pb_file));
787
788   } else {
789     enum Events {
790       kInitializeEvent,
791       kRenderEvent,
792       kCaptureEvent,
793       kResetEventDeprecated
794     };
795     int16_t event = 0;
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());
801
802       if (simulating) {
803         if (far_file == NULL) {
804           event = kCaptureEvent;
805         } else {
806           if (event == kRenderEvent) {
807             event = kCaptureEvent;
808           } else {
809             event = kRenderEvent;
810           }
811         }
812       } else {
813         read_count = fread(&event, sizeof(event), 1, event_file);
814         if (read_count != 1) {
815           break;
816         }
817       }
818
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;
824
825       if (event == kInitializeEvent || event == kResetEventDeprecated) {
826         ASSERT_EQ(1u,
827             fread(&sample_rate_hz, sizeof(sample_rate_hz), 1, event_file));
828         samples_per_channel = sample_rate_hz / 100;
829
830         ASSERT_EQ(1u,
831             fread(&device_sample_rate_hz,
832                   sizeof(device_sample_rate_hz),
833                   1,
834                   event_file));
835
836         ASSERT_EQ(apm->kNoError,
837             apm->set_sample_rate_hz(sample_rate_hz));
838
839         ASSERT_EQ(apm->kNoError,
840                   apm->echo_cancellation()->set_device_sample_rate_hz(
841                       device_sample_rate_hz));
842
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;
848
849         if (verbose) {
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);
853         }
854
855       } else if (event == kRenderEvent) {
856         reverse_count++;
857
858         size_t size = samples_per_channel * num_render_channels;
859         read_count = fread(far_frame.data_,
860                            sizeof(int16_t),
861                            size,
862                            far_file);
863
864         if (simulating) {
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),
869                       SEEK_CUR));
870             break; // This is expected.
871           }
872         } else {
873           ASSERT_EQ(size, read_count);
874         }
875
876         if (perf_testing) {
877           t0 = TickTime::Now();
878         }
879
880         ASSERT_EQ(apm->kNoError,
881                   apm->AnalyzeReverseStream(&far_frame));
882
883         if (perf_testing) {
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();
889           }
890           if (tick_diff.Microseconds() < min_time_reverse_us) {
891             min_time_reverse_us = tick_diff.Microseconds();
892           }
893         }
894
895       } else if (event == kCaptureEvent) {
896         primary_count++;
897         near_frame.num_channels_ = num_capture_input_channels;
898
899         size_t size = samples_per_channel * num_capture_input_channels;
900         read_count = fread(near_frame.data_,
901                            sizeof(int16_t),
902                            size,
903                            near_file);
904
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);
909           fflush(stdout);
910         }
911         if (simulating) {
912           if (read_count != size) {
913             break; // This is expected.
914           }
915
916           delay_ms = 0;
917           drift_samples = 0;
918         } else {
919           ASSERT_EQ(size, read_count);
920
921           // TODO(ajm): sizeof(delay_ms) for current files?
922           ASSERT_EQ(1u,
923               fread(&delay_ms, 2, 1, delay_file));
924           ASSERT_EQ(1u,
925               fread(&drift_samples, sizeof(drift_samples), 1, drift_file));
926         }
927
928         if (apm->gain_control()->is_enabled() &&
929             apm->gain_control()->mode() == GainControl::kAdaptiveAnalog) {
930           SimulateMic(capture_level, &near_frame);
931         }
932
933         if (perf_testing) {
934           t0 = TickTime::Now();
935         }
936
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;
943         }
944         ASSERT_EQ(apm->kNoError,
945                   apm->set_stream_delay_ms(delay_ms));
946         apm->echo_cancellation()->set_stream_drift_samples(drift_samples);
947
948         int err = apm->ProcessStream(&near_frame);
949         if (err == apm->kBadStreamParameterWarning) {
950           printf("Bad parameter warning. %s\n", trace_stream.str().c_str());
951         }
952         ASSERT_TRUE(err == apm->kNoError ||
953                     err == apm->kBadStreamParameterWarning);
954         ASSERT_TRUE(near_frame.num_channels_ == apm->num_output_channels());
955
956         capture_level = apm->gain_control()->stream_analog_level();
957
958         stream_has_voice =
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),
963                                1,
964                                vad_out_file));
965         }
966
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),
971                                1,
972                                ns_prob_file));
973         }
974
975         if (apm->gain_control()->mode() != GainControl::kAdaptiveAnalog) {
976           ASSERT_EQ(capture_level_in, capture_level);
977         }
978
979         if (perf_testing) {
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();
985           }
986           if (tick_diff.Microseconds() < min_time_us) {
987             min_time_us = tick_diff.Microseconds();
988           }
989         }
990
991         size = samples_per_channel * near_frame.num_channels_;
992         ASSERT_EQ(size, fwrite(near_frame.data_,
993                                sizeof(int16_t),
994                                size,
995                                out_file));
996       }
997       else {
998         FAIL() << "Event " << event << " is unrecognized";
999       }
1000     }
1001   }
1002   printf("100%% complete\r");
1003
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(),
1010                                 sizeof(char),
1011                                 path_size,
1012                                 aecm_echo_path_out_file));
1013     fclose(aecm_echo_path_out_file);
1014     aecm_echo_path_out_file = NULL;
1015   }
1016
1017   if (verbose) {
1018     printf("\nProcessed frames: %d (primary), %d (reverse)\n",
1019         primary_count, reverse_count);
1020
1021     if (apm->level_estimator()->is_enabled()) {
1022       printf("\n--Level metrics--\n");
1023       printf("RMS: %d dBFS\n", -apm->level_estimator()->RMS());
1024     }
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");
1030       printf("ERL:  ");
1031       PrintStat(metrics.echo_return_loss);
1032       printf("ERLE: ");
1033       PrintStat(metrics.echo_return_loss_enhancement);
1034       printf("ANLP: ");
1035       PrintStat(metrics.a_nlp);
1036     }
1037     if (apm->echo_cancellation()->is_delay_logging_enabled()) {
1038       int median = 0;
1039       int std = 0;
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);
1044     }
1045   }
1046
1047   if (!pb_file) {
1048     int8_t temp_int8;
1049     if (far_file) {
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";
1052     }
1053
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";
1056
1057     if (!simulating) {
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";
1064     }
1065   }
1066
1067   if (perf_testing) {
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),"
1073              " %.3f ms (min)\n",
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);
1080     } else {
1081       printf("Warning: no capture frames\n");
1082     }
1083   }
1084 }
1085 }  // namespace
1086
1087 int main(int argc, char* argv[])
1088 {
1089   void_main(argc, argv);
1090
1091   // Optional, but removes memory leak noise from Valgrind.
1092   google::protobuf::ShutdownProtobufLibrary();
1093   return 0;
1094 }