blob: 6278a210093c91a133e88e2f10efb42097df3dca [file] [log] [blame]
andrew@webrtc.org08df9b22014-12-16 20:57:15 +00001/*
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>
mgraczyk@chromium.org4ddde2e2015-01-29 22:39:44 +000012#include <sstream>
mgraczyk@chromium.org5a92b782015-01-15 01:28:36 +000013#include <string>
andrew@webrtc.org08df9b22014-12-16 20:57:15 +000014
15#include "gflags/gflags.h"
16#include "webrtc/base/checks.h"
kwiberg@webrtc.org00b8f6b2015-02-26 14:34:55 +000017#include "webrtc/base/scoped_ptr.h"
kjellander@webrtc.org035e9122015-01-28 19:57:00 +000018#include "webrtc/common_audio/channel_buffer.h"
andrew@webrtc.org08df9b22014-12-16 20:57:15 +000019#include "webrtc/common_audio/wav_file.h"
andrew@webrtc.org08df9b22014-12-16 20:57:15 +000020#include "webrtc/modules/audio_processing/include/audio_processing.h"
21#include "webrtc/modules/audio_processing/test/test_utils.h"
Alejandro Luebs5d22c002015-04-15 11:26:40 -070022#include "webrtc/system_wrappers/interface/tick_util.h"
andrew@webrtc.org08df9b22014-12-16 20:57:15 +000023
24DEFINE_string(dump, "", "The name of the debug dump file to read from.");
25DEFINE_string(c, "", "The name of the capture input file to read from.");
26DEFINE_string(o, "out.wav", "Name of the capture output file to write to.");
27DEFINE_int32(o_channels, 0, "Number of output channels. Defaults to input.");
28DEFINE_int32(o_sample_rate, 0, "Output sample rate in Hz. Defaults to input.");
mgraczyk@chromium.org5a92b782015-01-15 01:28:36 +000029DEFINE_double(mic_spacing, 0.0,
mgraczyk@chromium.org4ddde2e2015-01-29 22:39:44 +000030 "Alternate way to specify mic_positions. "
31 "Assumes uniform linear array with specified spacings.");
32DEFINE_string(mic_positions, "",
33 "Space delimited cartesian coordinates of microphones in meters. "
34 "The coordinates of each point are contiguous. "
35 "For a two element array: \"x1 y1 z1 x2 y2 z2\"");
andrew@webrtc.org08df9b22014-12-16 20:57:15 +000036
37DEFINE_bool(aec, false, "Enable echo cancellation.");
38DEFINE_bool(agc, false, "Enable automatic gain control.");
39DEFINE_bool(hpf, false, "Enable high-pass filtering.");
40DEFINE_bool(ns, false, "Enable noise suppression.");
41DEFINE_bool(ts, false, "Enable transient suppression.");
mgraczyk@chromium.org5a92b782015-01-15 01:28:36 +000042DEFINE_bool(bf, false, "Enable beamforming.");
andrew@webrtc.org08df9b22014-12-16 20:57:15 +000043DEFINE_bool(all, false, "Enable all components.");
44
45DEFINE_int32(ns_level, -1, "Noise suppression level [0 - 3].");
46
Alejandro Luebs5d22c002015-04-15 11:26:40 -070047DEFINE_bool(perf, false, "Enable performance tests.");
48
andrew@webrtc.org08df9b22014-12-16 20:57:15 +000049static const int kChunksPerSecond = 100;
50static const char kUsage[] =
51 "Command-line tool to run audio processing on WAV files. Accepts either\n"
52 "an input capture WAV file or protobuf debug dump and writes to an output\n"
53 "WAV file.\n"
54 "\n"
55 "All components are disabled by default. If any bi-directional components\n"
56 "are enabled, only debug dump files are permitted.";
57
58namespace webrtc {
59
mgraczyk@chromium.org4ddde2e2015-01-29 22:39:44 +000060namespace {
61
62// Returns a vector<T> parsed from whitespace delimited values in to_parse,
63// or an empty vector if the string could not be parsed.
64template<typename T>
65std::vector<T> parse_list(std::string to_parse) {
66 std::vector<T> values;
67
68 std::istringstream str(to_parse);
69 std::copy(
70 std::istream_iterator<T>(str),
71 std::istream_iterator<T>(),
72 std::back_inserter(values));
73
74 return values;
75}
76
77// Parses the array geometry from the command line.
78//
79// If a vector with size != num_mics is returned, an error has occurred and an
80// appropriate error message has been printed to stdout.
81std::vector<Point> get_array_geometry(size_t num_mics) {
82 std::vector<Point> result;
83 result.reserve(num_mics);
84
85 if (FLAGS_mic_positions.length()) {
86 CHECK(FLAGS_mic_spacing == 0.0 &&
87 "mic_positions and mic_spacing should not both be specified");
88
89 const std::vector<float> values = parse_list<float>(FLAGS_mic_positions);
90 if (values.size() != 3 * num_mics) {
91 fprintf(stderr,
92 "Could not parse mic_positions or incorrect number of points.\n");
93 } else {
94 for (size_t i = 0; i < values.size(); i += 3) {
95 double x = values[i + 0];
96 double y = values[i + 1];
97 double z = values[i + 2];
98 result.push_back(Point(x, y, z));
99 }
100 }
101 } else {
102 if (FLAGS_mic_spacing <= 0) {
103 fprintf(stderr,
104 "mic_spacing must a positive value when beamforming is enabled.\n");
105 } else {
106 for (size_t i = 0; i < num_mics; ++i) {
aluebs@webrtc.org1d883942015-03-05 20:38:21 +0000107 result.push_back(Point(i * FLAGS_mic_spacing, 0.f, 0.f));
mgraczyk@chromium.org4ddde2e2015-01-29 22:39:44 +0000108 }
109 }
110 }
111
112 return result;
113}
114
115} // namespace
116
andrew@webrtc.org08df9b22014-12-16 20:57:15 +0000117int main(int argc, char* argv[]) {
118 {
119 const std::string program_name = argv[0];
120 const std::string usage = kUsage;
121 google::SetUsageMessage(usage);
122 }
123 google::ParseCommandLineFlags(&argc, &argv, true);
124
125 if (!((FLAGS_c == "") ^ (FLAGS_dump == ""))) {
126 fprintf(stderr,
127 "An input file must be specified with either -c or -dump.\n");
128 return 1;
129 }
130 if (FLAGS_dump != "") {
131 fprintf(stderr, "FIXME: the -dump option is not yet implemented.\n");
132 return 1;
133 }
134
135 WavReader c_file(FLAGS_c);
136 // If the output format is uninitialized, use the input format.
137 int o_channels = FLAGS_o_channels;
138 if (!o_channels)
139 o_channels = c_file.num_channels();
140 int o_sample_rate = FLAGS_o_sample_rate;
141 if (!o_sample_rate)
142 o_sample_rate = c_file.sample_rate();
143 WavWriter o_file(FLAGS_o, o_sample_rate, o_channels);
144
andrew@webrtc.org08df9b22014-12-16 20:57:15 +0000145 Config config;
146 config.Set<ExperimentalNs>(new ExperimentalNs(FLAGS_ts || FLAGS_all));
mgraczyk@chromium.org5a92b782015-01-15 01:28:36 +0000147
148 if (FLAGS_bf || FLAGS_all) {
mgraczyk@chromium.org5a92b782015-01-15 01:28:36 +0000149 const size_t num_mics = c_file.num_channels();
mgraczyk@chromium.org4ddde2e2015-01-29 22:39:44 +0000150 const std::vector<Point> array_geometry = get_array_geometry(num_mics);
151 if (array_geometry.size() != num_mics) {
152 return 1;
mgraczyk@chromium.org5a92b782015-01-15 01:28:36 +0000153 }
154
155 config.Set<Beamforming>(new Beamforming(true, array_geometry));
156 }
157
kwiberg@webrtc.org00b8f6b2015-02-26 14:34:55 +0000158 rtc::scoped_ptr<AudioProcessing> ap(AudioProcessing::Create(config));
andrew@webrtc.org08df9b22014-12-16 20:57:15 +0000159 if (FLAGS_dump != "") {
160 CHECK_EQ(kNoErr, ap->echo_cancellation()->Enable(FLAGS_aec || FLAGS_all));
161 } else if (FLAGS_aec) {
162 fprintf(stderr, "-aec requires a -dump file.\n");
163 return -1;
164 }
165 CHECK_EQ(kNoErr, ap->gain_control()->Enable(FLAGS_agc || FLAGS_all));
166 CHECK_EQ(kNoErr, ap->gain_control()->set_mode(GainControl::kFixedDigital));
167 CHECK_EQ(kNoErr, ap->high_pass_filter()->Enable(FLAGS_hpf || FLAGS_all));
168 CHECK_EQ(kNoErr, ap->noise_suppression()->Enable(FLAGS_ns || FLAGS_all));
169 if (FLAGS_ns_level != -1)
170 CHECK_EQ(kNoErr, ap->noise_suppression()->set_level(
171 static_cast<NoiseSuppression::Level>(FLAGS_ns_level)));
172
mgraczyk@chromium.org5a92b782015-01-15 01:28:36 +0000173 printf("Input file: %s\nChannels: %d, Sample rate: %d Hz\n\n",
174 FLAGS_c.c_str(), c_file.num_channels(), c_file.sample_rate());
175 printf("Output file: %s\nChannels: %d, Sample rate: %d Hz\n\n",
176 FLAGS_o.c_str(), o_file.num_channels(), o_file.sample_rate());
177
andrew@webrtc.org08df9b22014-12-16 20:57:15 +0000178 ChannelBuffer<float> c_buf(c_file.sample_rate() / kChunksPerSecond,
179 c_file.num_channels());
180 ChannelBuffer<float> o_buf(o_file.sample_rate() / kChunksPerSecond,
181 o_file.num_channels());
182
aluebs@webrtc.orgd35a5c32015-02-10 22:52:15 +0000183 const size_t c_length =
184 static_cast<size_t>(c_buf.num_channels() * c_buf.num_frames());
185 const size_t o_length =
186 static_cast<size_t>(o_buf.num_channels() * o_buf.num_frames());
kwiberg@webrtc.org00b8f6b2015-02-26 14:34:55 +0000187 rtc::scoped_ptr<float[]> c_interleaved(new float[c_length]);
188 rtc::scoped_ptr<float[]> o_interleaved(new float[o_length]);
Alejandro Luebs5d22c002015-04-15 11:26:40 -0700189 TickTime processing_start_time;
190 TickInterval accumulated_time;
191 int num_chunks = 0;
andrew@webrtc.org08df9b22014-12-16 20:57:15 +0000192 while (c_file.ReadSamples(c_length, c_interleaved.get()) == c_length) {
193 FloatS16ToFloat(c_interleaved.get(), c_length, c_interleaved.get());
aluebs@webrtc.orgd35a5c32015-02-10 22:52:15 +0000194 Deinterleave(c_interleaved.get(), c_buf.num_frames(),
andrew@webrtc.org08df9b22014-12-16 20:57:15 +0000195 c_buf.num_channels(), c_buf.channels());
Alejandro Luebs5d22c002015-04-15 11:26:40 -0700196 if (FLAGS_perf) {
197 processing_start_time = TickTime::Now();
198 }
andrew@webrtc.org08df9b22014-12-16 20:57:15 +0000199 CHECK_EQ(kNoErr,
200 ap->ProcessStream(c_buf.channels(),
aluebs@webrtc.orgd35a5c32015-02-10 22:52:15 +0000201 c_buf.num_frames(),
andrew@webrtc.org08df9b22014-12-16 20:57:15 +0000202 c_file.sample_rate(),
203 LayoutFromChannels(c_buf.num_channels()),
204 o_file.sample_rate(),
205 LayoutFromChannels(o_buf.num_channels()),
206 o_buf.channels()));
Alejandro Luebs5d22c002015-04-15 11:26:40 -0700207 if (FLAGS_perf) {
208 accumulated_time += TickTime::Now() - processing_start_time;
209 }
aluebs@webrtc.orgd35a5c32015-02-10 22:52:15 +0000210 Interleave(o_buf.channels(), o_buf.num_frames(),
andrew@webrtc.org08df9b22014-12-16 20:57:15 +0000211 o_buf.num_channels(), o_interleaved.get());
aluebs@webrtc.orgd35a5c32015-02-10 22:52:15 +0000212 FloatToFloatS16(o_interleaved.get(), o_length, o_interleaved.get());
213 o_file.WriteSamples(o_interleaved.get(), o_length);
Alejandro Luebs5d22c002015-04-15 11:26:40 -0700214 num_chunks++;
andrew@webrtc.org08df9b22014-12-16 20:57:15 +0000215 }
Alejandro Luebs5d22c002015-04-15 11:26:40 -0700216 if (FLAGS_perf) {
217 printf("Execution time: %.3fs\nFile time: %.2fs\n\n",
218 accumulated_time.Milliseconds() * 0.001f, num_chunks * 0.01f);
219 }
andrew@webrtc.org08df9b22014-12-16 20:57:15 +0000220 return 0;
221}
222
223} // namespace webrtc
224
225int main(int argc, char* argv[]) {
226 return webrtc::main(argc, argv);
227}