Home | History | Annotate | Download | only in test
      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.
     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 
     68 void PrintStat(const AudioProcessing::Statistic& stat) {
     69   printf("%d, %d, %d\n", stat.average,
     70                          stat.maximum,
     71                          stat.minimum);
     72 }
     73 
     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.
    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 
    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