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