Home | History | Annotate | Download | only in test
      1 /*
      2  *  Copyright (c) 2014 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 <iostream>
     13 #include <sstream>
     14 #include <string>
     15 #include <utility>
     16 
     17 #include "gflags/gflags.h"
     18 #include "webrtc/base/checks.h"
     19 #include "webrtc/base/format_macros.h"
     20 #include "webrtc/base/scoped_ptr.h"
     21 #include "webrtc/common_audio/channel_buffer.h"
     22 #include "webrtc/common_audio/wav_file.h"
     23 #include "webrtc/modules/audio_processing/include/audio_processing.h"
     24 #include "webrtc/modules/audio_processing/test/audio_file_processor.h"
     25 #include "webrtc/modules/audio_processing/test/protobuf_utils.h"
     26 #include "webrtc/modules/audio_processing/test/test_utils.h"
     27 #include "webrtc/system_wrappers/include/tick_util.h"
     28 #include "webrtc/test/testsupport/trace_to_stderr.h"
     29 
     30 namespace {
     31 
     32 bool ValidateOutChannels(const char* flagname, int32_t value) {
     33   return value >= 0;
     34 }
     35 
     36 }  // namespace
     37 
     38 DEFINE_string(dump, "", "Name of the aecdump debug file to read from.");
     39 DEFINE_string(i, "", "Name of the capture input stream file to read from.");
     40 DEFINE_string(
     41     o,
     42     "out.wav",
     43     "Name of the output file to write the processed capture stream to.");
     44 DEFINE_int32(out_channels, 1, "Number of output channels.");
     45 const bool out_channels_dummy =
     46     google::RegisterFlagValidator(&FLAGS_out_channels, &ValidateOutChannels);
     47 DEFINE_int32(out_sample_rate, 48000, "Output sample rate in Hz.");
     48 DEFINE_string(mic_positions, "",
     49     "Space delimited cartesian coordinates of microphones in meters. "
     50     "The coordinates of each point are contiguous. "
     51     "For a two element array: \"x1 y1 z1 x2 y2 z2\"");
     52 DEFINE_double(
     53     target_angle_degrees,
     54     90,
     55     "The azimuth of the target in degrees. Only applies to beamforming.");
     56 
     57 DEFINE_bool(aec, false, "Enable echo cancellation.");
     58 DEFINE_bool(agc, false, "Enable automatic gain control.");
     59 DEFINE_bool(hpf, false, "Enable high-pass filtering.");
     60 DEFINE_bool(ns, false, "Enable noise suppression.");
     61 DEFINE_bool(ts, false, "Enable transient suppression.");
     62 DEFINE_bool(bf, false, "Enable beamforming.");
     63 DEFINE_bool(ie, false, "Enable intelligibility enhancer.");
     64 DEFINE_bool(all, false, "Enable all components.");
     65 
     66 DEFINE_int32(ns_level, -1, "Noise suppression level [0 - 3].");
     67 
     68 DEFINE_bool(perf, false, "Enable performance tests.");
     69 
     70 namespace webrtc {
     71 namespace {
     72 
     73 const int kChunksPerSecond = 100;
     74 const char kUsage[] =
     75     "Command-line tool to run audio processing on WAV files. Accepts either\n"
     76     "an input capture WAV file or protobuf debug dump and writes to an output\n"
     77     "WAV file.\n"
     78     "\n"
     79     "All components are disabled by default. If any bi-directional components\n"
     80     "are enabled, only debug dump files are permitted.";
     81 
     82 }  // namespace
     83 
     84 int main(int argc, char* argv[]) {
     85   google::SetUsageMessage(kUsage);
     86   google::ParseCommandLineFlags(&argc, &argv, true);
     87 
     88   if (!((FLAGS_i.empty()) ^ (FLAGS_dump.empty()))) {
     89     fprintf(stderr,
     90             "An input file must be specified with either -i or -dump.\n");
     91     return 1;
     92   }
     93   if (FLAGS_dump.empty() && (FLAGS_aec || FLAGS_ie)) {
     94     fprintf(stderr, "-aec and -ie require a -dump file.\n");
     95     return 1;
     96   }
     97   if (FLAGS_ie) {
     98     fprintf(stderr,
     99             "FIXME(ajm): The intelligibility enhancer output is not dumped.\n");
    100     return 1;
    101   }
    102 
    103   test::TraceToStderr trace_to_stderr(true);
    104   Config config;
    105   if (FLAGS_bf || FLAGS_all) {
    106     if (FLAGS_mic_positions.empty()) {
    107       fprintf(stderr, "-mic_positions must be specified when -bf is used.\n");
    108       return 1;
    109     }
    110     config.Set<Beamforming>(new Beamforming(
    111         true, ParseArrayGeometry(FLAGS_mic_positions),
    112         SphericalPointf(DegreesToRadians(FLAGS_target_angle_degrees), 0.f,
    113                         1.f)));
    114   }
    115   config.Set<ExperimentalNs>(new ExperimentalNs(FLAGS_ts || FLAGS_all));
    116   config.Set<Intelligibility>(new Intelligibility(FLAGS_ie || FLAGS_all));
    117 
    118   rtc::scoped_ptr<AudioProcessing> ap(AudioProcessing::Create(config));
    119   RTC_CHECK_EQ(kNoErr, ap->echo_cancellation()->Enable(FLAGS_aec || FLAGS_all));
    120   RTC_CHECK_EQ(kNoErr, ap->gain_control()->Enable(FLAGS_agc || FLAGS_all));
    121   RTC_CHECK_EQ(kNoErr, ap->high_pass_filter()->Enable(FLAGS_hpf || FLAGS_all));
    122   RTC_CHECK_EQ(kNoErr, ap->noise_suppression()->Enable(FLAGS_ns || FLAGS_all));
    123   if (FLAGS_ns_level != -1) {
    124     RTC_CHECK_EQ(kNoErr,
    125                  ap->noise_suppression()->set_level(
    126                      static_cast<NoiseSuppression::Level>(FLAGS_ns_level)));
    127   }
    128   ap->set_stream_key_pressed(FLAGS_ts);
    129 
    130   rtc::scoped_ptr<AudioFileProcessor> processor;
    131   auto out_file = rtc_make_scoped_ptr(new WavWriter(
    132       FLAGS_o, FLAGS_out_sample_rate, static_cast<size_t>(FLAGS_out_channels)));
    133   std::cout << FLAGS_o << ": " << out_file->FormatAsString() << std::endl;
    134   if (FLAGS_dump.empty()) {
    135     auto in_file = rtc_make_scoped_ptr(new WavReader(FLAGS_i));
    136     std::cout << FLAGS_i << ": " << in_file->FormatAsString() << std::endl;
    137     processor.reset(new WavFileProcessor(std::move(ap), std::move(in_file),
    138                                          std::move(out_file)));
    139 
    140   } else {
    141     processor.reset(new AecDumpFileProcessor(
    142         std::move(ap), fopen(FLAGS_dump.c_str(), "rb"), std::move(out_file)));
    143   }
    144 
    145   int num_chunks = 0;
    146   while (processor->ProcessChunk()) {
    147     trace_to_stderr.SetTimeSeconds(num_chunks * 1.f / kChunksPerSecond);
    148     ++num_chunks;
    149   }
    150 
    151   if (FLAGS_perf) {
    152     const auto& proc_time = processor->proc_time();
    153     int64_t exec_time_us = proc_time.sum.Microseconds();
    154     printf(
    155         "\nExecution time: %.3f s, File time: %.2f s\n"
    156         "Time per chunk (mean, max, min):\n%.0f us, %.0f us, %.0f us\n",
    157         exec_time_us * 1e-6, num_chunks * 1.f / kChunksPerSecond,
    158         exec_time_us * 1.f / num_chunks, 1.f * proc_time.max.Microseconds(),
    159         1.f * proc_time.min.Microseconds());
    160   }
    161 
    162   return 0;
    163 }
    164 
    165 }  // namespace webrtc
    166 
    167 int main(int argc, char* argv[]) {
    168   return webrtc::main(argc, argv);
    169 }
    170