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
30using webrtc::AudioFrame;
31using webrtc::AudioProcessing;
32using webrtc::EchoCancellation;
33using webrtc::GainControl;
34using webrtc::NoiseSuppression;
35using webrtc::scoped_array;
36using webrtc::TickInterval;
37using webrtc::TickTime;
38
39using webrtc::audioproc::Event;
40using webrtc::audioproc::Init;
41using webrtc::audioproc::ReverseStream;
42using webrtc::audioproc::Stream;
43
44namespace {
45// Returns true on success, false on error or end-of-file.
46bool 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
68void PrintStat(const AudioProcessing::Statistic& stat) {
69  printf("%d, %d, %d\n", stat.average,
70                         stat.maximum,
71                         stat.minimum);
72}
73
74void 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.
133void 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
957int 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