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
30namespace {
31
32bool ValidateOutChannels(const char* flagname, int32_t value) {
33  return value >= 0;
34}
35
36}  // namespace
37
38DEFINE_string(dump, "", "Name of the aecdump debug file to read from.");
39DEFINE_string(i, "", "Name of the capture input stream file to read from.");
40DEFINE_string(
41    o,
42    "out.wav",
43    "Name of the output file to write the processed capture stream to.");
44DEFINE_int32(out_channels, 1, "Number of output channels.");
45const bool out_channels_dummy =
46    google::RegisterFlagValidator(&FLAGS_out_channels, &ValidateOutChannels);
47DEFINE_int32(out_sample_rate, 48000, "Output sample rate in Hz.");
48DEFINE_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\"");
52DEFINE_double(
53    target_angle_degrees,
54    90,
55    "The azimuth of the target in degrees. Only applies to beamforming.");
56
57DEFINE_bool(aec, false, "Enable echo cancellation.");
58DEFINE_bool(agc, false, "Enable automatic gain control.");
59DEFINE_bool(hpf, false, "Enable high-pass filtering.");
60DEFINE_bool(ns, false, "Enable noise suppression.");
61DEFINE_bool(ts, false, "Enable transient suppression.");
62DEFINE_bool(bf, false, "Enable beamforming.");
63DEFINE_bool(ie, false, "Enable intelligibility enhancer.");
64DEFINE_bool(all, false, "Enable all components.");
65
66DEFINE_int32(ns_level, -1, "Noise suppression level [0 - 3].");
67
68DEFINE_bool(perf, false, "Enable performance tests.");
69
70namespace webrtc {
71namespace {
72
73const int kChunksPerSecond = 100;
74const 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
84int 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
167int main(int argc, char* argv[]) {
168  return webrtc::main(argc, argv);
169}
170