Upstream version 7.36.149.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/audio_processing/test/test_utils.h"
23 #include "webrtc/modules/interface/module_common_types.h"
24 #include "webrtc/system_wrappers/interface/cpu_features_wrapper.h"
25 #include "webrtc/system_wrappers/interface/scoped_ptr.h"
26 #include "webrtc/system_wrappers/interface/tick_util.h"
27 #include "webrtc/test/testsupport/fileutils.h"
28 #include "webrtc/test/testsupport/perf_test.h"
29 #ifdef WEBRTC_ANDROID_PLATFORM_BUILD
30 #include "gtest/gtest.h"
31 #include "external/webrtc/webrtc/modules/audio_processing/debug.pb.h"
32 #else
33 #include "testing/gtest/include/gtest/gtest.h"
34 #include "webrtc/audio_processing/debug.pb.h"
35 #endif
36
37 namespace webrtc {
38
39 using webrtc::audioproc::Event;
40 using webrtc::audioproc::Init;
41 using webrtc::audioproc::ReverseStream;
42 using webrtc::audioproc::Stream;
43
44 namespace {
45
46 void PrintStat(const AudioProcessing::Statistic& stat) {
47   printf("%d, %d, %d\n", stat.average,
48                          stat.maximum,
49                          stat.minimum);
50 }
51
52 void usage() {
53   printf(
54   "Usage: process_test [options] [-pb PROTOBUF_FILE]\n"
55   "  [-ir REVERSE_FILE] [-i PRIMARY_FILE] [-o OUT_FILE]\n");
56   printf(
57   "process_test is a test application for AudioProcessing.\n\n"
58   "When a protobuf debug file is available, specify it with -pb. Alternately,\n"
59   "when -ir or -i is used, the specified files will be processed directly in\n"
60   "a simulation mode. Otherwise the full set of legacy test files is expected\n"
61   "to be present in the working directory. OUT_FILE should be specified\n"
62   "without extension to support both int and float output.\n\n");
63   printf("Options\n");
64   printf("General configuration (only used for the simulation mode):\n");
65   printf("  -fs SAMPLE_RATE_HZ\n");
66   printf("  -ch CHANNELS_IN CHANNELS_OUT\n");
67   printf("  -rch REVERSE_CHANNELS\n");
68   printf("\n");
69   printf("Component configuration:\n");
70   printf(
71   "All components are disabled by default. Each block below begins with a\n"
72   "flag to enable the component with default settings. The subsequent flags\n"
73   "in the block are used to provide configuration settings.\n");
74   printf("\n  -aec     Echo cancellation\n");
75   printf("  --drift_compensation\n");
76   printf("  --no_drift_compensation\n");
77   printf("  --no_echo_metrics\n");
78   printf("  --no_delay_logging\n");
79   printf("  --aec_suppression_level LEVEL  [0 - 2]\n");
80   printf("  --extended_filter\n");
81   printf("\n  -aecm    Echo control mobile\n");
82   printf("  --aecm_echo_path_in_file FILE\n");
83   printf("  --aecm_echo_path_out_file FILE\n");
84   printf("  --no_comfort_noise\n");
85   printf("  --routing_mode MODE  [0 - 4]\n");
86   printf("\n  -agc     Gain control\n");
87   printf("  --analog\n");
88   printf("  --adaptive_digital\n");
89   printf("  --fixed_digital\n");
90   printf("  --target_level LEVEL\n");
91   printf("  --compression_gain GAIN\n");
92   printf("  --limiter\n");
93   printf("  --no_limiter\n");
94   printf("\n  -hpf     High pass filter\n");
95   printf("\n  -ns      Noise suppression\n");
96   printf("  --ns_low\n");
97   printf("  --ns_moderate\n");
98   printf("  --ns_high\n");
99   printf("  --ns_very_high\n");
100   printf("  --ns_prob_file FILE\n");
101   printf("\n  -vad     Voice activity detection\n");
102   printf("  --vad_out_file FILE\n");
103   printf("\n  -expns   Experimental noise suppression\n");
104   printf("\n Level metrics (enabled by default)\n");
105   printf("  --no_level_metrics\n");
106   printf("\n");
107   printf("Modifiers:\n");
108   printf("  --noasm            Disable SSE optimization.\n");
109   printf("  --add_delay DELAY  Add DELAY ms to input value.\n");
110   printf("  --delay DELAY      Override input delay with DELAY ms.\n");
111   printf("  --perf             Measure performance.\n");
112   printf("  --quiet            Suppress text output.\n");
113   printf("  --no_progress      Suppress progress.\n");
114   printf("  --debug_file FILE  Dump a debug recording.\n");
115 }
116
117 static float MicLevel2Gain(int level) {
118   return pow(10.0f, ((level - 127.0f) / 128.0f * 40.0f) / 20.0f);
119 }
120
121 static void SimulateMic(int mic_level, AudioFrame* frame) {
122   mic_level = std::min(std::max(mic_level, 0), 255);
123   float mic_gain = MicLevel2Gain(mic_level);
124   int num_samples = frame->samples_per_channel_ * frame->num_channels_;
125   float v;
126   for (int n = 0; n < num_samples; n++) {
127     v = floor(frame->data_[n] * mic_gain + 0.5);
128     v = std::max(std::min(32767.0f, v), -32768.0f);
129     frame->data_[n] = static_cast<int16_t>(v);
130   }
131 }
132
133 // void function for gtest.
134 void void_main(int argc, char* argv[]) {
135   if (argc > 1 && strcmp(argv[1], "--help") == 0) {
136     usage();
137     return;
138   }
139
140   if (argc < 2) {
141     printf("Did you mean to run without arguments?\n");
142     printf("Try `process_test --help' for more information.\n\n");
143   }
144
145   scoped_ptr<AudioProcessing> apm(AudioProcessing::Create());
146   ASSERT_TRUE(apm.get() != NULL);
147
148   const char* pb_filename = NULL;
149   const char* far_filename = NULL;
150   const char* near_filename = NULL;
151   std::string out_filename;
152   const char* vad_out_filename = NULL;
153   const char* ns_prob_filename = NULL;
154   const char* aecm_echo_path_in_filename = NULL;
155   const char* aecm_echo_path_out_filename = NULL;
156
157   int32_t sample_rate_hz = 16000;
158
159   int num_capture_input_channels = 1;
160   int num_capture_output_channels = 1;
161   int num_render_channels = 1;
162
163   int samples_per_channel = sample_rate_hz / 100;
164
165   bool simulating = false;
166   bool perf_testing = false;
167   bool verbose = true;
168   bool progress = true;
169   int extra_delay_ms = 0;
170   int override_delay_ms = 0;
171
172   ASSERT_EQ(apm->kNoError, apm->level_estimator()->Enable(true));
173   for (int i = 1; i < argc; i++) {
174     if (strcmp(argv[i], "-pb") == 0) {
175       i++;
176       ASSERT_LT(i, argc) << "Specify protobuf filename after -pb";
177       pb_filename = argv[i];
178
179     } else if (strcmp(argv[i], "-ir") == 0) {
180       i++;
181       ASSERT_LT(i, argc) << "Specify filename after -ir";
182       far_filename = argv[i];
183       simulating = true;
184
185     } else if (strcmp(argv[i], "-i") == 0) {
186       i++;
187       ASSERT_LT(i, argc) << "Specify filename after -i";
188       near_filename = argv[i];
189       simulating = true;
190
191     } else if (strcmp(argv[i], "-o") == 0) {
192       i++;
193       ASSERT_LT(i, argc) << "Specify filename without extension after -o";
194       out_filename = argv[i];
195
196     } else if (strcmp(argv[i], "-fs") == 0) {
197       i++;
198       ASSERT_LT(i, argc) << "Specify sample rate after -fs";
199       ASSERT_EQ(1, sscanf(argv[i], "%d", &sample_rate_hz));
200       samples_per_channel = sample_rate_hz / 100;
201
202     } else if (strcmp(argv[i], "-ch") == 0) {
203       i++;
204       ASSERT_LT(i + 1, argc) << "Specify number of channels after -ch";
205       ASSERT_EQ(1, sscanf(argv[i], "%d", &num_capture_input_channels));
206       i++;
207       ASSERT_EQ(1, sscanf(argv[i], "%d", &num_capture_output_channels));
208
209     } else if (strcmp(argv[i], "-rch") == 0) {
210       i++;
211       ASSERT_LT(i, argc) << "Specify number of channels after -rch";
212       ASSERT_EQ(1, sscanf(argv[i], "%d", &num_render_channels));
213
214     } else if (strcmp(argv[i], "-aec") == 0) {
215       ASSERT_EQ(apm->kNoError, apm->echo_cancellation()->Enable(true));
216       ASSERT_EQ(apm->kNoError,
217                 apm->echo_cancellation()->enable_metrics(true));
218       ASSERT_EQ(apm->kNoError,
219                 apm->echo_cancellation()->enable_delay_logging(true));
220
221     } else if (strcmp(argv[i], "--drift_compensation") == 0) {
222       ASSERT_EQ(apm->kNoError, apm->echo_cancellation()->Enable(true));
223       // TODO(ajm): this is enabled in the VQE test app by default. Investigate
224       //            why it can give better performance despite passing zeros.
225       ASSERT_EQ(apm->kNoError,
226                 apm->echo_cancellation()->enable_drift_compensation(true));
227     } else if (strcmp(argv[i], "--no_drift_compensation") == 0) {
228       ASSERT_EQ(apm->kNoError, apm->echo_cancellation()->Enable(true));
229       ASSERT_EQ(apm->kNoError,
230                 apm->echo_cancellation()->enable_drift_compensation(false));
231
232     } else if (strcmp(argv[i], "--no_echo_metrics") == 0) {
233       ASSERT_EQ(apm->kNoError, apm->echo_cancellation()->Enable(true));
234       ASSERT_EQ(apm->kNoError,
235                 apm->echo_cancellation()->enable_metrics(false));
236
237     } else if (strcmp(argv[i], "--no_delay_logging") == 0) {
238       ASSERT_EQ(apm->kNoError, apm->echo_cancellation()->Enable(true));
239       ASSERT_EQ(apm->kNoError,
240                 apm->echo_cancellation()->enable_delay_logging(false));
241
242     } else if (strcmp(argv[i], "--no_level_metrics") == 0) {
243       ASSERT_EQ(apm->kNoError, apm->level_estimator()->Enable(false));
244
245     } else if (strcmp(argv[i], "--aec_suppression_level") == 0) {
246       i++;
247       ASSERT_LT(i, argc) << "Specify level after --aec_suppression_level";
248       int suppression_level;
249       ASSERT_EQ(1, sscanf(argv[i], "%d", &suppression_level));
250       ASSERT_EQ(apm->kNoError,
251                 apm->echo_cancellation()->set_suppression_level(
252                     static_cast<webrtc::EchoCancellation::SuppressionLevel>(
253                         suppression_level)));
254
255     } else if (strcmp(argv[i], "--extended_filter") == 0) {
256       Config config;
257       config.Set<DelayCorrection>(new DelayCorrection(true));
258       apm->SetExtraOptions(config);
259
260     } else if (strcmp(argv[i], "-aecm") == 0) {
261       ASSERT_EQ(apm->kNoError, apm->echo_control_mobile()->Enable(true));
262
263     } else if (strcmp(argv[i], "--aecm_echo_path_in_file") == 0) {
264       i++;
265       ASSERT_LT(i, argc) << "Specify filename after --aecm_echo_path_in_file";
266       aecm_echo_path_in_filename = argv[i];
267
268     } else if (strcmp(argv[i], "--aecm_echo_path_out_file") == 0) {
269       i++;
270       ASSERT_LT(i, argc) << "Specify filename after --aecm_echo_path_out_file";
271       aecm_echo_path_out_filename = argv[i];
272
273     } else if (strcmp(argv[i], "--no_comfort_noise") == 0) {
274       ASSERT_EQ(apm->kNoError,
275                 apm->echo_control_mobile()->enable_comfort_noise(false));
276
277     } else if (strcmp(argv[i], "--routing_mode") == 0) {
278       i++;
279       ASSERT_LT(i, argc) << "Specify mode after --routing_mode";
280       int routing_mode;
281       ASSERT_EQ(1, sscanf(argv[i], "%d", &routing_mode));
282       ASSERT_EQ(apm->kNoError,
283                 apm->echo_control_mobile()->set_routing_mode(
284                     static_cast<webrtc::EchoControlMobile::RoutingMode>(
285                         routing_mode)));
286
287     } else if (strcmp(argv[i], "-agc") == 0) {
288       ASSERT_EQ(apm->kNoError, apm->gain_control()->Enable(true));
289
290     } else if (strcmp(argv[i], "--analog") == 0) {
291       ASSERT_EQ(apm->kNoError, apm->gain_control()->Enable(true));
292       ASSERT_EQ(apm->kNoError,
293                 apm->gain_control()->set_mode(GainControl::kAdaptiveAnalog));
294
295     } else if (strcmp(argv[i], "--adaptive_digital") == 0) {
296       ASSERT_EQ(apm->kNoError, apm->gain_control()->Enable(true));
297       ASSERT_EQ(apm->kNoError,
298                 apm->gain_control()->set_mode(GainControl::kAdaptiveDigital));
299
300     } else if (strcmp(argv[i], "--fixed_digital") == 0) {
301       ASSERT_EQ(apm->kNoError, apm->gain_control()->Enable(true));
302       ASSERT_EQ(apm->kNoError,
303                 apm->gain_control()->set_mode(GainControl::kFixedDigital));
304
305     } else if (strcmp(argv[i], "--target_level") == 0) {
306       i++;
307       int level;
308       ASSERT_EQ(1, sscanf(argv[i], "%d", &level));
309
310       ASSERT_EQ(apm->kNoError, apm->gain_control()->Enable(true));
311       ASSERT_EQ(apm->kNoError,
312                 apm->gain_control()->set_target_level_dbfs(level));
313
314     } else if (strcmp(argv[i], "--compression_gain") == 0) {
315       i++;
316       int gain;
317       ASSERT_EQ(1, sscanf(argv[i], "%d", &gain));
318
319       ASSERT_EQ(apm->kNoError, apm->gain_control()->Enable(true));
320       ASSERT_EQ(apm->kNoError,
321                 apm->gain_control()->set_compression_gain_db(gain));
322
323     } else if (strcmp(argv[i], "--limiter") == 0) {
324       ASSERT_EQ(apm->kNoError, apm->gain_control()->Enable(true));
325       ASSERT_EQ(apm->kNoError,
326                 apm->gain_control()->enable_limiter(true));
327
328     } else if (strcmp(argv[i], "--no_limiter") == 0) {
329       ASSERT_EQ(apm->kNoError, apm->gain_control()->Enable(true));
330       ASSERT_EQ(apm->kNoError,
331                 apm->gain_control()->enable_limiter(false));
332
333     } else if (strcmp(argv[i], "-hpf") == 0) {
334       ASSERT_EQ(apm->kNoError, apm->high_pass_filter()->Enable(true));
335
336     } else if (strcmp(argv[i], "-ns") == 0) {
337       ASSERT_EQ(apm->kNoError, apm->noise_suppression()->Enable(true));
338
339     } else if (strcmp(argv[i], "--ns_low") == 0) {
340       ASSERT_EQ(apm->kNoError, apm->noise_suppression()->Enable(true));
341       ASSERT_EQ(apm->kNoError,
342           apm->noise_suppression()->set_level(NoiseSuppression::kLow));
343
344     } else if (strcmp(argv[i], "--ns_moderate") == 0) {
345       ASSERT_EQ(apm->kNoError, apm->noise_suppression()->Enable(true));
346       ASSERT_EQ(apm->kNoError,
347           apm->noise_suppression()->set_level(NoiseSuppression::kModerate));
348
349     } else if (strcmp(argv[i], "--ns_high") == 0) {
350       ASSERT_EQ(apm->kNoError, apm->noise_suppression()->Enable(true));
351       ASSERT_EQ(apm->kNoError,
352           apm->noise_suppression()->set_level(NoiseSuppression::kHigh));
353
354     } else if (strcmp(argv[i], "--ns_very_high") == 0) {
355       ASSERT_EQ(apm->kNoError, apm->noise_suppression()->Enable(true));
356       ASSERT_EQ(apm->kNoError,
357           apm->noise_suppression()->set_level(NoiseSuppression::kVeryHigh));
358
359     } else if (strcmp(argv[i], "--ns_prob_file") == 0) {
360       i++;
361       ASSERT_LT(i, argc) << "Specify filename after --ns_prob_file";
362       ns_prob_filename = argv[i];
363
364     } else if (strcmp(argv[i], "-vad") == 0) {
365       ASSERT_EQ(apm->kNoError, apm->voice_detection()->Enable(true));
366
367     } else if (strcmp(argv[i], "--vad_very_low") == 0) {
368       ASSERT_EQ(apm->kNoError, apm->voice_detection()->Enable(true));
369       ASSERT_EQ(apm->kNoError,
370           apm->voice_detection()->set_likelihood(
371               VoiceDetection::kVeryLowLikelihood));
372
373     } else if (strcmp(argv[i], "--vad_low") == 0) {
374       ASSERT_EQ(apm->kNoError, apm->voice_detection()->Enable(true));
375       ASSERT_EQ(apm->kNoError,
376           apm->voice_detection()->set_likelihood(
377               VoiceDetection::kLowLikelihood));
378
379     } else if (strcmp(argv[i], "--vad_moderate") == 0) {
380       ASSERT_EQ(apm->kNoError, apm->voice_detection()->Enable(true));
381       ASSERT_EQ(apm->kNoError,
382           apm->voice_detection()->set_likelihood(
383               VoiceDetection::kModerateLikelihood));
384
385     } else if (strcmp(argv[i], "--vad_high") == 0) {
386       ASSERT_EQ(apm->kNoError, apm->voice_detection()->Enable(true));
387       ASSERT_EQ(apm->kNoError,
388           apm->voice_detection()->set_likelihood(
389               VoiceDetection::kHighLikelihood));
390
391     } else if (strcmp(argv[i], "--vad_out_file") == 0) {
392       i++;
393       ASSERT_LT(i, argc) << "Specify filename after --vad_out_file";
394       vad_out_filename = argv[i];
395
396     } else if (strcmp(argv[i], "-expns") == 0) {
397       ASSERT_EQ(apm->kNoError, apm->EnableExperimentalNs(true));
398
399     } else if (strcmp(argv[i], "--noasm") == 0) {
400       WebRtc_GetCPUInfo = WebRtc_GetCPUInfoNoASM;
401       // We need to reinitialize here if components have already been enabled.
402       ASSERT_EQ(apm->kNoError, apm->Initialize());
403
404     } else if (strcmp(argv[i], "--add_delay") == 0) {
405       i++;
406       ASSERT_EQ(1, sscanf(argv[i], "%d", &extra_delay_ms));
407
408     } else if (strcmp(argv[i], "--delay") == 0) {
409       i++;
410       ASSERT_EQ(1, sscanf(argv[i], "%d", &override_delay_ms));
411
412     } else if (strcmp(argv[i], "--perf") == 0) {
413       perf_testing = true;
414
415     } else if (strcmp(argv[i], "--quiet") == 0) {
416       verbose = false;
417       progress = false;
418
419     } else if (strcmp(argv[i], "--no_progress") == 0) {
420       progress = false;
421
422     } else if (strcmp(argv[i], "--debug_file") == 0) {
423       i++;
424       ASSERT_LT(i, argc) << "Specify filename after --debug_file";
425       ASSERT_EQ(apm->kNoError, apm->StartDebugRecording(argv[i]));
426     } else {
427       FAIL() << "Unrecognized argument " << argv[i];
428     }
429   }
430   // If we're reading a protobuf file, ensure a simulation hasn't also
431   // been requested (which makes no sense...)
432   ASSERT_FALSE(pb_filename && simulating);
433
434   if (verbose) {
435     printf("Sample rate: %d Hz\n", sample_rate_hz);
436     printf("Primary channels: %d (in), %d (out)\n",
437            num_capture_input_channels,
438            num_capture_output_channels);
439     printf("Reverse channels: %d \n", num_render_channels);
440   }
441
442   const std::string out_path = webrtc::test::OutputPath();
443   const char far_file_default[] = "apm_far.pcm";
444   const char near_file_default[] = "apm_near.pcm";
445   const char event_filename[] = "apm_event.dat";
446   const char delay_filename[] = "apm_delay.dat";
447   const char drift_filename[] = "apm_drift.dat";
448   const std::string vad_file_default = out_path + "vad_out.dat";
449   const std::string ns_prob_file_default = out_path + "ns_prob.dat";
450
451   if (!simulating) {
452     far_filename = far_file_default;
453     near_filename = near_file_default;
454   }
455
456   if (out_filename.size() == 0) {
457     out_filename = out_path + "out";
458   }
459   std::string out_float_filename = out_filename + ".float";
460   out_filename += ".pcm";
461
462   if (!vad_out_filename) {
463     vad_out_filename = vad_file_default.c_str();
464   }
465
466   if (!ns_prob_filename) {
467     ns_prob_filename = ns_prob_file_default.c_str();
468   }
469
470   FILE* pb_file = NULL;
471   FILE* far_file = NULL;
472   FILE* near_file = NULL;
473   FILE* event_file = NULL;
474   FILE* delay_file = NULL;
475   FILE* drift_file = NULL;
476   FILE* vad_out_file = NULL;
477   FILE* ns_prob_file = NULL;
478   FILE* aecm_echo_path_in_file = NULL;
479   FILE* aecm_echo_path_out_file = NULL;
480
481   if (pb_filename) {
482     pb_file = OpenFile(pb_filename, "rb");
483   } else {
484     if (far_filename) {
485       far_file = OpenFile(far_filename, "rb");
486     }
487
488     near_file = OpenFile(near_filename, "rb");
489     if (!simulating) {
490       event_file = OpenFile(event_filename, "rb");
491       delay_file = OpenFile(delay_filename, "rb");
492       drift_file = OpenFile(drift_filename, "rb");
493     }
494   }
495
496   int near_size_bytes = 0;
497   if (pb_file) {
498     struct stat st;
499     stat(pb_filename, &st);
500     // Crude estimate, but should be good enough.
501     near_size_bytes = st.st_size / 3;
502   } else {
503     struct stat st;
504     stat(near_filename, &st);
505     near_size_bytes = st.st_size;
506   }
507
508   if (apm->voice_detection()->is_enabled()) {
509     vad_out_file = OpenFile(vad_out_filename, "wb");
510   }
511
512   if (apm->noise_suppression()->is_enabled()) {
513     ns_prob_file = OpenFile(ns_prob_filename, "wb");
514   }
515
516   if (aecm_echo_path_in_filename != NULL) {
517     aecm_echo_path_in_file = OpenFile(aecm_echo_path_in_filename, "rb");
518
519     const size_t path_size =
520         apm->echo_control_mobile()->echo_path_size_bytes();
521     scoped_ptr<char[]> echo_path(new char[path_size]);
522     ASSERT_EQ(path_size, fread(echo_path.get(),
523                                sizeof(char),
524                                path_size,
525                                aecm_echo_path_in_file));
526     EXPECT_EQ(apm->kNoError,
527               apm->echo_control_mobile()->SetEchoPath(echo_path.get(),
528                                                       path_size));
529     fclose(aecm_echo_path_in_file);
530     aecm_echo_path_in_file = NULL;
531   }
532
533   if (aecm_echo_path_out_filename != NULL) {
534     aecm_echo_path_out_file = OpenFile(aecm_echo_path_out_filename, "wb");
535   }
536
537   size_t read_count = 0;
538   int reverse_count = 0;
539   int primary_count = 0;
540   int near_read_bytes = 0;
541   TickInterval acc_ticks;
542
543   AudioFrame far_frame;
544   AudioFrame near_frame;
545
546   int delay_ms = 0;
547   int drift_samples = 0;
548   int capture_level = 127;
549   int8_t stream_has_voice = 0;
550   float ns_speech_prob = 0.0f;
551
552   TickTime t0 = TickTime::Now();
553   TickTime t1 = t0;
554   int64_t max_time_us = 0;
555   int64_t max_time_reverse_us = 0;
556   int64_t min_time_us = 1e6;
557   int64_t min_time_reverse_us = 1e6;
558
559   // TODO(ajm): Ideally we would refactor this block into separate functions,
560   //            but for now we want to share the variables.
561   if (pb_file) {
562     Event event_msg;
563     scoped_ptr<ChannelBuffer<float> > reverse_cb;
564     scoped_ptr<ChannelBuffer<float> > primary_cb;
565     int output_sample_rate = 32000;
566     AudioProcessing::ChannelLayout output_layout = AudioProcessing::kMono;
567     while (ReadMessageFromFile(pb_file, &event_msg)) {
568       std::ostringstream trace_stream;
569       trace_stream << "Processed frames: " << reverse_count << " (reverse), "
570                    << primary_count << " (primary)";
571       SCOPED_TRACE(trace_stream.str());
572
573       if (event_msg.type() == Event::INIT) {
574         ASSERT_TRUE(event_msg.has_init());
575         const Init msg = event_msg.init();
576
577         ASSERT_TRUE(msg.has_sample_rate());
578         ASSERT_TRUE(msg.has_num_input_channels());
579         ASSERT_TRUE(msg.has_num_output_channels());
580         ASSERT_TRUE(msg.has_num_reverse_channels());
581         int reverse_sample_rate = msg.sample_rate();
582         if (msg.has_reverse_sample_rate()) {
583           reverse_sample_rate = msg.reverse_sample_rate();
584         }
585         output_sample_rate = msg.sample_rate();
586         if (msg.has_output_sample_rate()) {
587           output_sample_rate = msg.output_sample_rate();
588         }
589         output_layout = LayoutFromChannels(msg.num_output_channels());
590         ASSERT_EQ(kNoErr, apm->Initialize(
591                               msg.sample_rate(),
592                               output_sample_rate,
593                               reverse_sample_rate,
594                               LayoutFromChannels(msg.num_input_channels()),
595                               output_layout,
596                               LayoutFromChannels(msg.num_reverse_channels())));
597
598         samples_per_channel = msg.sample_rate() / 100;
599         far_frame.sample_rate_hz_ = msg.sample_rate();
600         far_frame.samples_per_channel_ = samples_per_channel;
601         far_frame.num_channels_ = msg.num_reverse_channels();
602         near_frame.sample_rate_hz_ = msg.sample_rate();
603         near_frame.samples_per_channel_ = samples_per_channel;
604         near_frame.num_channels_ = msg.num_input_channels();
605         reverse_cb.reset(new ChannelBuffer<float>(samples_per_channel,
606                                                   msg.num_reverse_channels()));
607         primary_cb.reset(new ChannelBuffer<float>(samples_per_channel,
608                                                   msg.num_input_channels()));
609
610         if (verbose) {
611           printf("Init at frame: %d (primary), %d (reverse)\n",
612               primary_count, reverse_count);
613           printf("  Primary rates: %d Hz (in), %d Hz (out)\n",
614                  msg.sample_rate(), output_sample_rate);
615           printf("  Primary channels: %d (in), %d (out)\n",
616                  msg.num_input_channels(),
617                  msg.num_output_channels());
618           printf("  Reverse rate: %d\n", reverse_sample_rate);
619           printf("  Reverse channels: %d\n", msg.num_reverse_channels());
620         }
621
622       } else if (event_msg.type() == Event::REVERSE_STREAM) {
623         ASSERT_TRUE(event_msg.has_reverse_stream());
624         ReverseStream msg = event_msg.reverse_stream();
625         reverse_count++;
626
627         ASSERT_TRUE(msg.has_data() ^ (msg.channel_size() > 0));
628         if (msg.has_data()) {
629           ASSERT_EQ(sizeof(int16_t) * samples_per_channel *
630               far_frame.num_channels_, msg.data().size());
631           memcpy(far_frame.data_, msg.data().data(), msg.data().size());
632         } else {
633           for (int i = 0; i < msg.channel_size(); ++i) {
634             reverse_cb->CopyFrom(msg.channel(i).data(), i);
635           }
636         }
637
638         if (perf_testing) {
639           t0 = TickTime::Now();
640         }
641
642         if (msg.has_data()) {
643           ASSERT_EQ(apm->kNoError,
644                     apm->AnalyzeReverseStream(&far_frame));
645         } else {
646           ASSERT_EQ(apm->kNoError,
647                     apm->AnalyzeReverseStream(
648                         reverse_cb->channels(),
649                         far_frame.samples_per_channel_,
650                         far_frame.sample_rate_hz_,
651                         LayoutFromChannels(far_frame.num_channels_)));
652         }
653
654         if (perf_testing) {
655           t1 = TickTime::Now();
656           TickInterval tick_diff = t1 - t0;
657           acc_ticks += tick_diff;
658           if (tick_diff.Microseconds() > max_time_reverse_us) {
659             max_time_reverse_us = tick_diff.Microseconds();
660           }
661           if (tick_diff.Microseconds() < min_time_reverse_us) {
662             min_time_reverse_us = tick_diff.Microseconds();
663           }
664         }
665
666       } else if (event_msg.type() == Event::STREAM) {
667         ASSERT_TRUE(event_msg.has_stream());
668         const Stream msg = event_msg.stream();
669         primary_count++;
670
671         // ProcessStream could have changed this for the output frame.
672         near_frame.num_channels_ = apm->num_input_channels();
673
674         ASSERT_TRUE(msg.has_input_data() ^ (msg.input_channel_size() > 0));
675         if (msg.has_input_data()) {
676           ASSERT_EQ(sizeof(int16_t) * samples_per_channel *
677               near_frame.num_channels_, msg.input_data().size());
678           memcpy(near_frame.data_,
679                  msg.input_data().data(),
680                  msg.input_data().size());
681         } else {
682           for (int i = 0; i < msg.input_channel_size(); ++i) {
683             primary_cb->CopyFrom(msg.input_channel(i).data(), i);
684           }
685         }
686
687         near_read_bytes += msg.input_data().size();
688         if (progress && primary_count % 100 == 0) {
689           printf("%.0f%% complete\r",
690               (near_read_bytes * 100.0) / near_size_bytes);
691           fflush(stdout);
692         }
693
694         if (perf_testing) {
695           t0 = TickTime::Now();
696         }
697
698         ASSERT_EQ(apm->kNoError,
699                   apm->gain_control()->set_stream_analog_level(msg.level()));
700         delay_ms = msg.delay() + extra_delay_ms;
701         if (override_delay_ms) {
702           delay_ms = override_delay_ms;
703         }
704         ASSERT_EQ(apm->kNoError,
705                   apm->set_stream_delay_ms(delay_ms));
706         apm->echo_cancellation()->set_stream_drift_samples(msg.drift());
707
708         if (msg.has_keypress()) {
709           apm->set_stream_key_pressed(msg.keypress());
710         } else {
711           apm->set_stream_key_pressed(true);
712         }
713
714         int err = apm->kNoError;
715         if (msg.has_input_data()) {
716           err = apm->ProcessStream(&near_frame);
717           ASSERT_TRUE(near_frame.num_channels_ == apm->num_output_channels());
718         } else {
719           err = apm->ProcessStream(
720               primary_cb->channels(),
721               near_frame.samples_per_channel_,
722               near_frame.sample_rate_hz_,
723               LayoutFromChannels(near_frame.num_channels_),
724               output_sample_rate,
725               output_layout,
726               primary_cb->channels());
727         }
728
729         if (err == apm->kBadStreamParameterWarning) {
730           printf("Bad parameter warning. %s\n", trace_stream.str().c_str());
731         }
732         ASSERT_TRUE(err == apm->kNoError ||
733                     err == apm->kBadStreamParameterWarning);
734
735         stream_has_voice =
736             static_cast<int8_t>(apm->voice_detection()->stream_has_voice());
737         if (vad_out_file != NULL) {
738           ASSERT_EQ(1u, fwrite(&stream_has_voice,
739                                sizeof(stream_has_voice),
740                                1,
741                                vad_out_file));
742         }
743
744         if (ns_prob_file != NULL) {
745           ns_speech_prob = apm->noise_suppression()->speech_probability();
746           ASSERT_EQ(1u, fwrite(&ns_speech_prob,
747                                sizeof(ns_speech_prob),
748                                1,
749                                ns_prob_file));
750         }
751
752         if (perf_testing) {
753           t1 = TickTime::Now();
754           TickInterval tick_diff = t1 - t0;
755           acc_ticks += tick_diff;
756           if (tick_diff.Microseconds() > max_time_us) {
757             max_time_us = tick_diff.Microseconds();
758           }
759           if (tick_diff.Microseconds() < min_time_us) {
760             min_time_us = tick_diff.Microseconds();
761           }
762         }
763
764         size_t num_samples = samples_per_channel * apm->num_output_channels();
765         if (msg.has_input_data()) {
766           static FILE* out_file = OpenFile(out_filename, "wb");
767           ASSERT_EQ(num_samples, fwrite(near_frame.data_,
768                                         sizeof(*near_frame.data_),
769                                         num_samples,
770                                         out_file));
771         } else {
772           static FILE* out_float_file = OpenFile(out_float_filename, "wb");
773           ASSERT_EQ(num_samples, fwrite(primary_cb->data(),
774                                         sizeof(*primary_cb->data()),
775                                         num_samples,
776                                         out_float_file));
777         }
778       }
779     }
780
781     ASSERT_TRUE(feof(pb_file));
782
783   } else {
784     enum Events {
785       kInitializeEvent,
786       kRenderEvent,
787       kCaptureEvent,
788       kResetEventDeprecated
789     };
790     int16_t event = 0;
791     while (simulating || feof(event_file) == 0) {
792       std::ostringstream trace_stream;
793       trace_stream << "Processed frames: " << reverse_count << " (reverse), "
794                    << primary_count << " (primary)";
795       SCOPED_TRACE(trace_stream.str());
796
797       if (simulating) {
798         if (far_file == NULL) {
799           event = kCaptureEvent;
800         } else {
801           if (event == kRenderEvent) {
802             event = kCaptureEvent;
803           } else {
804             event = kRenderEvent;
805           }
806         }
807       } else {
808         read_count = fread(&event, sizeof(event), 1, event_file);
809         if (read_count != 1) {
810           break;
811         }
812       }
813
814       far_frame.sample_rate_hz_ = sample_rate_hz;
815       far_frame.samples_per_channel_ = samples_per_channel;
816       far_frame.num_channels_ = num_render_channels;
817       near_frame.sample_rate_hz_ = sample_rate_hz;
818       near_frame.samples_per_channel_ = samples_per_channel;
819
820       if (event == kInitializeEvent || event == kResetEventDeprecated) {
821         ASSERT_EQ(1u,
822             fread(&sample_rate_hz, sizeof(sample_rate_hz), 1, event_file));
823         samples_per_channel = sample_rate_hz / 100;
824
825         int32_t unused_device_sample_rate_hz;
826         ASSERT_EQ(1u,
827             fread(&unused_device_sample_rate_hz,
828                   sizeof(unused_device_sample_rate_hz),
829                   1,
830                   event_file));
831
832         ASSERT_EQ(kNoErr, apm->Initialize(
833                               sample_rate_hz,
834                               sample_rate_hz,
835                               sample_rate_hz,
836                               LayoutFromChannels(num_capture_input_channels),
837                               LayoutFromChannels(num_capture_output_channels),
838                               LayoutFromChannels(num_render_channels)));
839
840         far_frame.sample_rate_hz_ = sample_rate_hz;
841         far_frame.samples_per_channel_ = samples_per_channel;
842         far_frame.num_channels_ = num_render_channels;
843         near_frame.sample_rate_hz_ = sample_rate_hz;
844         near_frame.samples_per_channel_ = samples_per_channel;
845
846         if (verbose) {
847           printf("Init at frame: %d (primary), %d (reverse)\n",
848               primary_count, reverse_count);
849           printf("  Sample rate: %d Hz\n", sample_rate_hz);
850         }
851
852       } else if (event == kRenderEvent) {
853         reverse_count++;
854
855         size_t size = samples_per_channel * num_render_channels;
856         read_count = fread(far_frame.data_,
857                            sizeof(int16_t),
858                            size,
859                            far_file);
860
861         if (simulating) {
862           if (read_count != size) {
863             // Read an equal amount from the near file to avoid errors due to
864             // not reaching end-of-file.
865             EXPECT_EQ(0, fseek(near_file, read_count * sizeof(int16_t),
866                       SEEK_CUR));
867             break; // This is expected.
868           }
869         } else {
870           ASSERT_EQ(size, read_count);
871         }
872
873         if (perf_testing) {
874           t0 = TickTime::Now();
875         }
876
877         ASSERT_EQ(apm->kNoError,
878                   apm->AnalyzeReverseStream(&far_frame));
879
880         if (perf_testing) {
881           t1 = TickTime::Now();
882           TickInterval tick_diff = t1 - t0;
883           acc_ticks += tick_diff;
884           if (tick_diff.Microseconds() > max_time_reverse_us) {
885             max_time_reverse_us = tick_diff.Microseconds();
886           }
887           if (tick_diff.Microseconds() < min_time_reverse_us) {
888             min_time_reverse_us = tick_diff.Microseconds();
889           }
890         }
891
892       } else if (event == kCaptureEvent) {
893         primary_count++;
894         near_frame.num_channels_ = num_capture_input_channels;
895
896         size_t size = samples_per_channel * num_capture_input_channels;
897         read_count = fread(near_frame.data_,
898                            sizeof(int16_t),
899                            size,
900                            near_file);
901
902         near_read_bytes += read_count * sizeof(int16_t);
903         if (progress && primary_count % 100 == 0) {
904           printf("%.0f%% complete\r",
905               (near_read_bytes * 100.0) / near_size_bytes);
906           fflush(stdout);
907         }
908         if (simulating) {
909           if (read_count != size) {
910             break; // This is expected.
911           }
912
913           delay_ms = 0;
914           drift_samples = 0;
915         } else {
916           ASSERT_EQ(size, read_count);
917
918           // TODO(ajm): sizeof(delay_ms) for current files?
919           ASSERT_EQ(1u,
920               fread(&delay_ms, 2, 1, delay_file));
921           ASSERT_EQ(1u,
922               fread(&drift_samples, sizeof(drift_samples), 1, drift_file));
923         }
924
925         if (apm->gain_control()->is_enabled() &&
926             apm->gain_control()->mode() == GainControl::kAdaptiveAnalog) {
927           SimulateMic(capture_level, &near_frame);
928         }
929
930         if (perf_testing) {
931           t0 = TickTime::Now();
932         }
933
934         const int capture_level_in = capture_level;
935         ASSERT_EQ(apm->kNoError,
936                   apm->gain_control()->set_stream_analog_level(capture_level));
937         delay_ms += extra_delay_ms;
938         if (override_delay_ms) {
939           delay_ms = override_delay_ms;
940         }
941         ASSERT_EQ(apm->kNoError,
942                   apm->set_stream_delay_ms(delay_ms));
943         apm->echo_cancellation()->set_stream_drift_samples(drift_samples);
944
945         apm->set_stream_key_pressed(true);
946
947         int err = apm->ProcessStream(&near_frame);
948         if (err == apm->kBadStreamParameterWarning) {
949           printf("Bad parameter warning. %s\n", trace_stream.str().c_str());
950         }
951         ASSERT_TRUE(err == apm->kNoError ||
952                     err == apm->kBadStreamParameterWarning);
953         ASSERT_TRUE(near_frame.num_channels_ == apm->num_output_channels());
954
955         capture_level = apm->gain_control()->stream_analog_level();
956
957         stream_has_voice =
958             static_cast<int8_t>(apm->voice_detection()->stream_has_voice());
959         if (vad_out_file != NULL) {
960           ASSERT_EQ(1u, fwrite(&stream_has_voice,
961                                sizeof(stream_has_voice),
962                                1,
963                                vad_out_file));
964         }
965
966         if (ns_prob_file != NULL) {
967           ns_speech_prob = apm->noise_suppression()->speech_probability();
968           ASSERT_EQ(1u, fwrite(&ns_speech_prob,
969                                sizeof(ns_speech_prob),
970                                1,
971                                ns_prob_file));
972         }
973
974         if (apm->gain_control()->mode() != GainControl::kAdaptiveAnalog) {
975           ASSERT_EQ(capture_level_in, capture_level);
976         }
977
978         if (perf_testing) {
979           t1 = TickTime::Now();
980           TickInterval tick_diff = t1 - t0;
981           acc_ticks += tick_diff;
982           if (tick_diff.Microseconds() > max_time_us) {
983             max_time_us = tick_diff.Microseconds();
984           }
985           if (tick_diff.Microseconds() < min_time_us) {
986             min_time_us = tick_diff.Microseconds();
987           }
988         }
989
990         size = samples_per_channel * near_frame.num_channels_;
991         static FILE* out_file = OpenFile(out_filename, "wb");
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_ptr<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
1086 }  // namespace
1087 }  // namespace webrtc
1088
1089 int main(int argc, char* argv[])
1090 {
1091   webrtc::void_main(argc, argv);
1092
1093   // Optional, but removes memory leak noise from Valgrind.
1094   google::protobuf::ShutdownProtobufLibrary();
1095   return 0;
1096 }