blob: 268702e5dec6ac7ebfff26d9d9f466bff876b04a [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"
Andrew MacDonaldcb05b722015-05-07 22:17:51 -070021#include "webrtc/modules/audio_processing/test/protobuf_utils.h"
andrew@webrtc.org08df9b22014-12-16 20:57:15 +000022#include "webrtc/modules/audio_processing/test/test_utils.h"
Alejandro Luebs5d22c002015-04-15 11:26:40 -070023#include "webrtc/system_wrappers/interface/tick_util.h"
Andrew MacDonaldb444b3f2015-05-27 17:26:03 -070024#include "webrtc/test/testsupport/trace_to_stderr.h"
andrew@webrtc.org08df9b22014-12-16 20:57:15 +000025
26DEFINE_string(dump, "", "The name of the debug dump file to read from.");
Andrew MacDonaldcb05b722015-05-07 22:17:51 -070027DEFINE_string(i, "", "The name of the input file to read from.");
ekmeyerson60d9b332015-08-14 10:35:55 -070028DEFINE_string(i_rev, "", "The name of the reverse input file to read from.");
Andrew MacDonaldcb05b722015-05-07 22:17:51 -070029DEFINE_string(o, "out.wav", "Name of the output file to write to.");
ekmeyerson60d9b332015-08-14 10:35:55 -070030DEFINE_string(o_rev,
31 "out_rev.wav",
32 "Name of the reverse output file to write to.");
Andrew MacDonaldcb05b722015-05-07 22:17:51 -070033DEFINE_int32(out_channels, 0, "Number of output channels. Defaults to input.");
34DEFINE_int32(out_sample_rate, 0,
35 "Output sample rate in Hz. Defaults to input.");
mgraczyk@chromium.org4ddde2e2015-01-29 22:39:44 +000036DEFINE_string(mic_positions, "",
37 "Space delimited cartesian coordinates of microphones in meters. "
38 "The coordinates of each point are contiguous. "
39 "For a two element array: \"x1 y1 z1 x2 y2 z2\"");
andrew@webrtc.org08df9b22014-12-16 20:57:15 +000040
41DEFINE_bool(aec, false, "Enable echo cancellation.");
42DEFINE_bool(agc, false, "Enable automatic gain control.");
43DEFINE_bool(hpf, false, "Enable high-pass filtering.");
44DEFINE_bool(ns, false, "Enable noise suppression.");
45DEFINE_bool(ts, false, "Enable transient suppression.");
mgraczyk@chromium.org5a92b782015-01-15 01:28:36 +000046DEFINE_bool(bf, false, "Enable beamforming.");
ekmeyerson60d9b332015-08-14 10:35:55 -070047DEFINE_bool(ie, false, "Enable intelligibility enhancer.");
andrew@webrtc.org08df9b22014-12-16 20:57:15 +000048DEFINE_bool(all, false, "Enable all components.");
49
50DEFINE_int32(ns_level, -1, "Noise suppression level [0 - 3].");
51
Alejandro Luebs5d22c002015-04-15 11:26:40 -070052DEFINE_bool(perf, false, "Enable performance tests.");
53
Andrew MacDonaldcb05b722015-05-07 22:17:51 -070054namespace webrtc {
55namespace {
56
57const int kChunksPerSecond = 100;
58const char kUsage[] =
andrew@webrtc.org08df9b22014-12-16 20:57:15 +000059 "Command-line tool to run audio processing on WAV files. Accepts either\n"
60 "an input capture WAV file or protobuf debug dump and writes to an output\n"
61 "WAV file.\n"
62 "\n"
63 "All components are disabled by default. If any bi-directional components\n"
64 "are enabled, only debug dump files are permitted.";
65
mgraczyk@chromium.org4ddde2e2015-01-29 22:39:44 +000066} // namespace
67
andrew@webrtc.org08df9b22014-12-16 20:57:15 +000068int main(int argc, char* argv[]) {
Andrew MacDonaldcb05b722015-05-07 22:17:51 -070069 google::SetUsageMessage(kUsage);
andrew@webrtc.org08df9b22014-12-16 20:57:15 +000070 google::ParseCommandLineFlags(&argc, &argv, true);
71
pbosbb36fdf2015-07-09 07:48:14 -070072 if (!((FLAGS_i.empty()) ^ (FLAGS_dump.empty()))) {
andrew@webrtc.org08df9b22014-12-16 20:57:15 +000073 fprintf(stderr,
Andrew MacDonaldcb05b722015-05-07 22:17:51 -070074 "An input file must be specified with either -i or -dump.\n");
andrew@webrtc.org08df9b22014-12-16 20:57:15 +000075 return 1;
76 }
pbosbb36fdf2015-07-09 07:48:14 -070077 if (!FLAGS_dump.empty()) {
andrew@webrtc.org08df9b22014-12-16 20:57:15 +000078 fprintf(stderr, "FIXME: the -dump option is not yet implemented.\n");
79 return 1;
80 }
81
Andrew MacDonaldb444b3f2015-05-27 17:26:03 -070082 test::TraceToStderr trace_to_stderr(true);
Andrew MacDonaldcb05b722015-05-07 22:17:51 -070083 WavReader in_file(FLAGS_i);
andrew@webrtc.org08df9b22014-12-16 20:57:15 +000084 // If the output format is uninitialized, use the input format.
Andrew MacDonaldcb05b722015-05-07 22:17:51 -070085 const int out_channels =
86 FLAGS_out_channels ? FLAGS_out_channels : in_file.num_channels();
87 const int out_sample_rate =
88 FLAGS_out_sample_rate ? FLAGS_out_sample_rate : in_file.sample_rate();
89 WavWriter out_file(FLAGS_o, out_sample_rate, out_channels);
andrew@webrtc.org08df9b22014-12-16 20:57:15 +000090
andrew@webrtc.org08df9b22014-12-16 20:57:15 +000091 Config config;
92 config.Set<ExperimentalNs>(new ExperimentalNs(FLAGS_ts || FLAGS_all));
ekmeyerson60d9b332015-08-14 10:35:55 -070093 config.Set<Intelligibility>(new Intelligibility(FLAGS_ie || FLAGS_all));
mgraczyk@chromium.org5a92b782015-01-15 01:28:36 +000094
95 if (FLAGS_bf || FLAGS_all) {
Andrew MacDonaldcb05b722015-05-07 22:17:51 -070096 const size_t num_mics = in_file.num_channels();
97 const std::vector<Point> array_geometry =
98 ParseArrayGeometry(FLAGS_mic_positions, num_mics);
99 CHECK_EQ(array_geometry.size(), num_mics);
mgraczyk@chromium.org5a92b782015-01-15 01:28:36 +0000100
101 config.Set<Beamforming>(new Beamforming(true, array_geometry));
102 }
103
kwiberg@webrtc.org00b8f6b2015-02-26 14:34:55 +0000104 rtc::scoped_ptr<AudioProcessing> ap(AudioProcessing::Create(config));
pbosbb36fdf2015-07-09 07:48:14 -0700105 if (!FLAGS_dump.empty()) {
andrew@webrtc.org08df9b22014-12-16 20:57:15 +0000106 CHECK_EQ(kNoErr, ap->echo_cancellation()->Enable(FLAGS_aec || FLAGS_all));
107 } else if (FLAGS_aec) {
108 fprintf(stderr, "-aec requires a -dump file.\n");
109 return -1;
110 }
ekmeyerson60d9b332015-08-14 10:35:55 -0700111 bool process_reverse = !FLAGS_i_rev.empty();
andrew@webrtc.org08df9b22014-12-16 20:57:15 +0000112 CHECK_EQ(kNoErr, ap->gain_control()->Enable(FLAGS_agc || FLAGS_all));
113 CHECK_EQ(kNoErr, ap->gain_control()->set_mode(GainControl::kFixedDigital));
114 CHECK_EQ(kNoErr, ap->high_pass_filter()->Enable(FLAGS_hpf || FLAGS_all));
115 CHECK_EQ(kNoErr, ap->noise_suppression()->Enable(FLAGS_ns || FLAGS_all));
116 if (FLAGS_ns_level != -1)
117 CHECK_EQ(kNoErr, ap->noise_suppression()->set_level(
118 static_cast<NoiseSuppression::Level>(FLAGS_ns_level)));
119
mgraczyk@chromium.org5a92b782015-01-15 01:28:36 +0000120 printf("Input file: %s\nChannels: %d, Sample rate: %d Hz\n\n",
Andrew MacDonaldcb05b722015-05-07 22:17:51 -0700121 FLAGS_i.c_str(), in_file.num_channels(), in_file.sample_rate());
mgraczyk@chromium.org5a92b782015-01-15 01:28:36 +0000122 printf("Output file: %s\nChannels: %d, Sample rate: %d Hz\n\n",
Andrew MacDonaldcb05b722015-05-07 22:17:51 -0700123 FLAGS_o.c_str(), out_file.num_channels(), out_file.sample_rate());
mgraczyk@chromium.org5a92b782015-01-15 01:28:36 +0000124
Andrew MacDonaldcb05b722015-05-07 22:17:51 -0700125 ChannelBuffer<float> in_buf(
126 rtc::CheckedDivExact(in_file.sample_rate(), kChunksPerSecond),
127 in_file.num_channels());
128 ChannelBuffer<float> out_buf(
129 rtc::CheckedDivExact(out_file.sample_rate(), kChunksPerSecond),
130 out_file.num_channels());
andrew@webrtc.org08df9b22014-12-16 20:57:15 +0000131
Andrew MacDonaldcb05b722015-05-07 22:17:51 -0700132 std::vector<float> in_interleaved(in_buf.size());
133 std::vector<float> out_interleaved(out_buf.size());
ekmeyerson60d9b332015-08-14 10:35:55 -0700134
135 rtc::scoped_ptr<WavReader> in_rev_file;
136 rtc::scoped_ptr<WavWriter> out_rev_file;
137 rtc::scoped_ptr<ChannelBuffer<float>> in_rev_buf;
138 rtc::scoped_ptr<ChannelBuffer<float>> out_rev_buf;
139 std::vector<float> in_rev_interleaved;
140 std::vector<float> out_rev_interleaved;
141 if (process_reverse) {
142 in_rev_file.reset(new WavReader(FLAGS_i_rev));
143 out_rev_file.reset(new WavWriter(FLAGS_o_rev, in_rev_file->sample_rate(),
144 in_rev_file->num_channels()));
145 printf("In rev file: %s\nChannels: %d, Sample rate: %d Hz\n\n",
146 FLAGS_i_rev.c_str(), in_rev_file->num_channels(),
147 in_rev_file->sample_rate());
148 printf("Out rev file: %s\nChannels: %d, Sample rate: %d Hz\n\n",
149 FLAGS_o_rev.c_str(), out_rev_file->num_channels(),
150 out_rev_file->sample_rate());
151 in_rev_buf.reset(new ChannelBuffer<float>(
152 rtc::CheckedDivExact(in_rev_file->sample_rate(), kChunksPerSecond),
153 in_rev_file->num_channels()));
154 in_rev_interleaved.resize(in_rev_buf->size());
155 out_rev_buf.reset(new ChannelBuffer<float>(
156 rtc::CheckedDivExact(out_rev_file->sample_rate(), kChunksPerSecond),
157 out_rev_file->num_channels()));
158 out_rev_interleaved.resize(out_rev_buf->size());
159 }
160
Alejandro Luebs5d22c002015-04-15 11:26:40 -0700161 TickTime processing_start_time;
162 TickInterval accumulated_time;
163 int num_chunks = 0;
Michael Graczyk86c6d332015-07-23 11:41:39 -0700164
165 const StreamConfig input_config = {
166 in_file.sample_rate(), in_buf.num_channels(),
167 };
168 const StreamConfig output_config = {
169 out_file.sample_rate(), out_buf.num_channels(),
170 };
Andrew MacDonaldc0775c02015-08-21 09:32:53 -0700171
172 StreamConfig reverse_input_config = {};
173 StreamConfig reverse_output_config = {};
174 if (process_reverse) {
175 StreamConfig reverse_input_config = {in_rev_file->sample_rate(),
176 in_rev_file->num_channels()};
177 StreamConfig reverse_output_config = {out_rev_file->sample_rate(),
178 out_rev_file->num_channels()};
179 }
Andrew MacDonaldcb05b722015-05-07 22:17:51 -0700180 while (in_file.ReadSamples(in_interleaved.size(),
181 &in_interleaved[0]) == in_interleaved.size()) {
Andrew MacDonaldb444b3f2015-05-27 17:26:03 -0700182 // Have logs display the file time rather than wallclock time.
183 trace_to_stderr.SetTimeSeconds(num_chunks * 1.f / kChunksPerSecond);
Andrew MacDonaldcb05b722015-05-07 22:17:51 -0700184 FloatS16ToFloat(&in_interleaved[0], in_interleaved.size(),
185 &in_interleaved[0]);
186 Deinterleave(&in_interleaved[0], in_buf.num_frames(),
187 in_buf.num_channels(), in_buf.channels());
ekmeyerson60d9b332015-08-14 10:35:55 -0700188 if (process_reverse) {
189 in_rev_file->ReadSamples(in_rev_interleaved.size(),
190 in_rev_interleaved.data());
191 FloatS16ToFloat(in_rev_interleaved.data(), in_rev_interleaved.size(),
192 in_rev_interleaved.data());
193 Deinterleave(in_rev_interleaved.data(), in_rev_buf->num_frames(),
194 in_rev_buf->num_channels(), in_rev_buf->channels());
195 }
Andrew MacDonaldcb05b722015-05-07 22:17:51 -0700196
Alejandro Luebs5d22c002015-04-15 11:26:40 -0700197 if (FLAGS_perf) {
198 processing_start_time = TickTime::Now();
199 }
Michael Graczyk86c6d332015-07-23 11:41:39 -0700200 CHECK_EQ(kNoErr, ap->ProcessStream(in_buf.channels(), input_config,
201 output_config, out_buf.channels()));
ekmeyerson60d9b332015-08-14 10:35:55 -0700202 if (process_reverse) {
203 CHECK_EQ(kNoErr, ap->ProcessReverseStream(
204 in_rev_buf->channels(), reverse_input_config,
205 reverse_output_config, out_rev_buf->channels()));
206 }
Alejandro Luebs5d22c002015-04-15 11:26:40 -0700207 if (FLAGS_perf) {
208 accumulated_time += TickTime::Now() - processing_start_time;
209 }
Andrew MacDonaldcb05b722015-05-07 22:17:51 -0700210
211 Interleave(out_buf.channels(), out_buf.num_frames(),
212 out_buf.num_channels(), &out_interleaved[0]);
213 FloatToFloatS16(&out_interleaved[0], out_interleaved.size(),
214 &out_interleaved[0]);
215 out_file.WriteSamples(&out_interleaved[0], out_interleaved.size());
ekmeyerson60d9b332015-08-14 10:35:55 -0700216 if (process_reverse) {
217 Interleave(out_rev_buf->channels(), out_rev_buf->num_frames(),
218 out_rev_buf->num_channels(), out_rev_interleaved.data());
219 FloatToFloatS16(out_rev_interleaved.data(), out_rev_interleaved.size(),
220 out_rev_interleaved.data());
221 out_rev_file->WriteSamples(out_rev_interleaved.data(),
222 out_rev_interleaved.size());
223 }
Alejandro Luebs5d22c002015-04-15 11:26:40 -0700224 num_chunks++;
andrew@webrtc.org08df9b22014-12-16 20:57:15 +0000225 }
Alejandro Luebs5d22c002015-04-15 11:26:40 -0700226 if (FLAGS_perf) {
Andrew MacDonaldb444b3f2015-05-27 17:26:03 -0700227 int64_t execution_time_ms = accumulated_time.Milliseconds();
228 printf("\nExecution time: %.3f s\nFile time: %.2f s\n"
229 "Time per chunk: %.3f ms\n",
230 execution_time_ms * 0.001f, num_chunks * 1.f / kChunksPerSecond,
231 execution_time_ms * 1.f / num_chunks);
Alejandro Luebs5d22c002015-04-15 11:26:40 -0700232 }
andrew@webrtc.org08df9b22014-12-16 20:57:15 +0000233 return 0;
234}
235
236} // namespace webrtc
237
238int main(int argc, char* argv[]) {
239 return webrtc::main(argc, argv);
240}