PageRenderTime 123ms CodeModel.GetById 2ms app.highlight 108ms RepoModel.GetById 1ms app.codeStats 0ms

/webrtc/modules/audio_processing/test/process_test.cc

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