blob: 811e9070fa57eac00935af240342f2cb2fab6c15 [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"
Henrik Kjellander98f53512015-10-28 18:17:40 +010023#include "webrtc/system_wrappers/include/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\"");
Alejandro Luebscb3f9bd2015-10-29 18:21:34 -070040DEFINE_double(target_angle_degrees, 90, "The azimuth of the target in radians");
andrew@webrtc.org08df9b22014-12-16 20:57:15 +000041
42DEFINE_bool(aec, false, "Enable echo cancellation.");
43DEFINE_bool(agc, false, "Enable automatic gain control.");
44DEFINE_bool(hpf, false, "Enable high-pass filtering.");
45DEFINE_bool(ns, false, "Enable noise suppression.");
46DEFINE_bool(ts, false, "Enable transient suppression.");
mgraczyk@chromium.org5a92b782015-01-15 01:28:36 +000047DEFINE_bool(bf, false, "Enable beamforming.");
ekmeyerson60d9b332015-08-14 10:35:55 -070048DEFINE_bool(ie, false, "Enable intelligibility enhancer.");
andrew@webrtc.org08df9b22014-12-16 20:57:15 +000049DEFINE_bool(all, false, "Enable all components.");
50
51DEFINE_int32(ns_level, -1, "Noise suppression level [0 - 3].");
52
Alejandro Luebs5d22c002015-04-15 11:26:40 -070053DEFINE_bool(perf, false, "Enable performance tests.");
54
Andrew MacDonaldcb05b722015-05-07 22:17:51 -070055namespace webrtc {
56namespace {
57
58const int kChunksPerSecond = 100;
59const char kUsage[] =
andrew@webrtc.org08df9b22014-12-16 20:57:15 +000060 "Command-line tool to run audio processing on WAV files. Accepts either\n"
61 "an input capture WAV file or protobuf debug dump and writes to an output\n"
62 "WAV file.\n"
63 "\n"
64 "All components are disabled by default. If any bi-directional components\n"
65 "are enabled, only debug dump files are permitted.";
66
Andrew MacDonaldbc2296d2015-08-24 17:29:26 -070067// Returns a StreamConfig corresponding to wav_file if it's non-nullptr.
68// Otherwise returns a default initialized StreamConfig.
69StreamConfig MakeStreamConfig(const WavFile* wav_file) {
70 if (wav_file) {
71 return {wav_file->sample_rate(), wav_file->num_channels()};
72 }
73 return {};
74}
75
mgraczyk@chromium.org4ddde2e2015-01-29 22:39:44 +000076} // namespace
77
andrew@webrtc.org08df9b22014-12-16 20:57:15 +000078int main(int argc, char* argv[]) {
Andrew MacDonaldcb05b722015-05-07 22:17:51 -070079 google::SetUsageMessage(kUsage);
andrew@webrtc.org08df9b22014-12-16 20:57:15 +000080 google::ParseCommandLineFlags(&argc, &argv, true);
81
pbosbb36fdf2015-07-09 07:48:14 -070082 if (!((FLAGS_i.empty()) ^ (FLAGS_dump.empty()))) {
andrew@webrtc.org08df9b22014-12-16 20:57:15 +000083 fprintf(stderr,
Andrew MacDonaldcb05b722015-05-07 22:17:51 -070084 "An input file must be specified with either -i or -dump.\n");
andrew@webrtc.org08df9b22014-12-16 20:57:15 +000085 return 1;
86 }
pbosbb36fdf2015-07-09 07:48:14 -070087 if (!FLAGS_dump.empty()) {
andrew@webrtc.org08df9b22014-12-16 20:57:15 +000088 fprintf(stderr, "FIXME: the -dump option is not yet implemented.\n");
89 return 1;
90 }
91
Andrew MacDonaldb444b3f2015-05-27 17:26:03 -070092 test::TraceToStderr trace_to_stderr(true);
Andrew MacDonaldcb05b722015-05-07 22:17:51 -070093 WavReader in_file(FLAGS_i);
andrew@webrtc.org08df9b22014-12-16 20:57:15 +000094 // If the output format is uninitialized, use the input format.
Andrew MacDonaldcb05b722015-05-07 22:17:51 -070095 const int out_channels =
96 FLAGS_out_channels ? FLAGS_out_channels : in_file.num_channels();
97 const int out_sample_rate =
98 FLAGS_out_sample_rate ? FLAGS_out_sample_rate : in_file.sample_rate();
99 WavWriter out_file(FLAGS_o, out_sample_rate, out_channels);
andrew@webrtc.org08df9b22014-12-16 20:57:15 +0000100
andrew@webrtc.org08df9b22014-12-16 20:57:15 +0000101 Config config;
102 config.Set<ExperimentalNs>(new ExperimentalNs(FLAGS_ts || FLAGS_all));
ekmeyerson60d9b332015-08-14 10:35:55 -0700103 config.Set<Intelligibility>(new Intelligibility(FLAGS_ie || FLAGS_all));
mgraczyk@chromium.org5a92b782015-01-15 01:28:36 +0000104
105 if (FLAGS_bf || FLAGS_all) {
Andrew MacDonaldcb05b722015-05-07 22:17:51 -0700106 const size_t num_mics = in_file.num_channels();
107 const std::vector<Point> array_geometry =
108 ParseArrayGeometry(FLAGS_mic_positions, num_mics);
henrikg91d6ede2015-09-17 00:24:34 -0700109 RTC_CHECK_EQ(array_geometry.size(), num_mics);
mgraczyk@chromium.org5a92b782015-01-15 01:28:36 +0000110
Alejandro Luebscb3f9bd2015-10-29 18:21:34 -0700111 config.Set<Beamforming>(new Beamforming(
112 true, array_geometry,
113 SphericalPointf(DegreesToRadians(FLAGS_target_angle_degrees), 0.f,
114 1.f)));
mgraczyk@chromium.org5a92b782015-01-15 01:28:36 +0000115 }
116
kwiberg@webrtc.org00b8f6b2015-02-26 14:34:55 +0000117 rtc::scoped_ptr<AudioProcessing> ap(AudioProcessing::Create(config));
pbosbb36fdf2015-07-09 07:48:14 -0700118 if (!FLAGS_dump.empty()) {
henrikg91d6ede2015-09-17 00:24:34 -0700119 RTC_CHECK_EQ(kNoErr,
120 ap->echo_cancellation()->Enable(FLAGS_aec || FLAGS_all));
andrew@webrtc.org08df9b22014-12-16 20:57:15 +0000121 } else if (FLAGS_aec) {
122 fprintf(stderr, "-aec requires a -dump file.\n");
123 return -1;
124 }
ekmeyerson60d9b332015-08-14 10:35:55 -0700125 bool process_reverse = !FLAGS_i_rev.empty();
henrikg91d6ede2015-09-17 00:24:34 -0700126 RTC_CHECK_EQ(kNoErr, ap->gain_control()->Enable(FLAGS_agc || FLAGS_all));
127 RTC_CHECK_EQ(kNoErr,
128 ap->gain_control()->set_mode(GainControl::kFixedDigital));
129 RTC_CHECK_EQ(kNoErr, ap->high_pass_filter()->Enable(FLAGS_hpf || FLAGS_all));
130 RTC_CHECK_EQ(kNoErr, ap->noise_suppression()->Enable(FLAGS_ns || FLAGS_all));
andrew@webrtc.org08df9b22014-12-16 20:57:15 +0000131 if (FLAGS_ns_level != -1)
henrikg91d6ede2015-09-17 00:24:34 -0700132 RTC_CHECK_EQ(kNoErr,
133 ap->noise_suppression()->set_level(
134 static_cast<NoiseSuppression::Level>(FLAGS_ns_level)));
andrew@webrtc.org08df9b22014-12-16 20:57:15 +0000135
mgraczyk@chromium.org5a92b782015-01-15 01:28:36 +0000136 printf("Input file: %s\nChannels: %d, Sample rate: %d Hz\n\n",
Andrew MacDonaldcb05b722015-05-07 22:17:51 -0700137 FLAGS_i.c_str(), in_file.num_channels(), in_file.sample_rate());
mgraczyk@chromium.org5a92b782015-01-15 01:28:36 +0000138 printf("Output file: %s\nChannels: %d, Sample rate: %d Hz\n\n",
Andrew MacDonaldcb05b722015-05-07 22:17:51 -0700139 FLAGS_o.c_str(), out_file.num_channels(), out_file.sample_rate());
mgraczyk@chromium.org5a92b782015-01-15 01:28:36 +0000140
Andrew MacDonaldcb05b722015-05-07 22:17:51 -0700141 ChannelBuffer<float> in_buf(
142 rtc::CheckedDivExact(in_file.sample_rate(), kChunksPerSecond),
143 in_file.num_channels());
144 ChannelBuffer<float> out_buf(
145 rtc::CheckedDivExact(out_file.sample_rate(), kChunksPerSecond),
146 out_file.num_channels());
andrew@webrtc.org08df9b22014-12-16 20:57:15 +0000147
Andrew MacDonaldcb05b722015-05-07 22:17:51 -0700148 std::vector<float> in_interleaved(in_buf.size());
149 std::vector<float> out_interleaved(out_buf.size());
ekmeyerson60d9b332015-08-14 10:35:55 -0700150
151 rtc::scoped_ptr<WavReader> in_rev_file;
152 rtc::scoped_ptr<WavWriter> out_rev_file;
153 rtc::scoped_ptr<ChannelBuffer<float>> in_rev_buf;
154 rtc::scoped_ptr<ChannelBuffer<float>> out_rev_buf;
155 std::vector<float> in_rev_interleaved;
156 std::vector<float> out_rev_interleaved;
157 if (process_reverse) {
158 in_rev_file.reset(new WavReader(FLAGS_i_rev));
159 out_rev_file.reset(new WavWriter(FLAGS_o_rev, in_rev_file->sample_rate(),
160 in_rev_file->num_channels()));
161 printf("In rev file: %s\nChannels: %d, Sample rate: %d Hz\n\n",
162 FLAGS_i_rev.c_str(), in_rev_file->num_channels(),
163 in_rev_file->sample_rate());
164 printf("Out rev file: %s\nChannels: %d, Sample rate: %d Hz\n\n",
165 FLAGS_o_rev.c_str(), out_rev_file->num_channels(),
166 out_rev_file->sample_rate());
167 in_rev_buf.reset(new ChannelBuffer<float>(
168 rtc::CheckedDivExact(in_rev_file->sample_rate(), kChunksPerSecond),
169 in_rev_file->num_channels()));
170 in_rev_interleaved.resize(in_rev_buf->size());
171 out_rev_buf.reset(new ChannelBuffer<float>(
172 rtc::CheckedDivExact(out_rev_file->sample_rate(), kChunksPerSecond),
173 out_rev_file->num_channels()));
174 out_rev_interleaved.resize(out_rev_buf->size());
175 }
176
Alejandro Luebs5d22c002015-04-15 11:26:40 -0700177 TickTime processing_start_time;
178 TickInterval accumulated_time;
179 int num_chunks = 0;
Michael Graczyk86c6d332015-07-23 11:41:39 -0700180
Andrew MacDonaldbc2296d2015-08-24 17:29:26 -0700181 const auto input_config = MakeStreamConfig(&in_file);
182 const auto output_config = MakeStreamConfig(&out_file);
183 const auto reverse_input_config = MakeStreamConfig(in_rev_file.get());
184 const auto reverse_output_config = MakeStreamConfig(out_rev_file.get());
Andrew MacDonaldc0775c02015-08-21 09:32:53 -0700185
Andrew MacDonaldcb05b722015-05-07 22:17:51 -0700186 while (in_file.ReadSamples(in_interleaved.size(),
187 &in_interleaved[0]) == in_interleaved.size()) {
Andrew MacDonaldb444b3f2015-05-27 17:26:03 -0700188 // Have logs display the file time rather than wallclock time.
189 trace_to_stderr.SetTimeSeconds(num_chunks * 1.f / kChunksPerSecond);
Andrew MacDonaldcb05b722015-05-07 22:17:51 -0700190 FloatS16ToFloat(&in_interleaved[0], in_interleaved.size(),
191 &in_interleaved[0]);
192 Deinterleave(&in_interleaved[0], in_buf.num_frames(),
193 in_buf.num_channels(), in_buf.channels());
ekmeyerson60d9b332015-08-14 10:35:55 -0700194 if (process_reverse) {
195 in_rev_file->ReadSamples(in_rev_interleaved.size(),
196 in_rev_interleaved.data());
197 FloatS16ToFloat(in_rev_interleaved.data(), in_rev_interleaved.size(),
198 in_rev_interleaved.data());
199 Deinterleave(in_rev_interleaved.data(), in_rev_buf->num_frames(),
200 in_rev_buf->num_channels(), in_rev_buf->channels());
201 }
Andrew MacDonaldcb05b722015-05-07 22:17:51 -0700202
Alejandro Luebs5d22c002015-04-15 11:26:40 -0700203 if (FLAGS_perf) {
204 processing_start_time = TickTime::Now();
205 }
henrikg91d6ede2015-09-17 00:24:34 -0700206 RTC_CHECK_EQ(kNoErr, ap->ProcessStream(in_buf.channels(), input_config,
207 output_config, out_buf.channels()));
ekmeyerson60d9b332015-08-14 10:35:55 -0700208 if (process_reverse) {
henrikg91d6ede2015-09-17 00:24:34 -0700209 RTC_CHECK_EQ(kNoErr, ap->ProcessReverseStream(
210 in_rev_buf->channels(), reverse_input_config,
211 reverse_output_config, out_rev_buf->channels()));
ekmeyerson60d9b332015-08-14 10:35:55 -0700212 }
Alejandro Luebs5d22c002015-04-15 11:26:40 -0700213 if (FLAGS_perf) {
214 accumulated_time += TickTime::Now() - processing_start_time;
215 }
Andrew MacDonaldcb05b722015-05-07 22:17:51 -0700216
217 Interleave(out_buf.channels(), out_buf.num_frames(),
218 out_buf.num_channels(), &out_interleaved[0]);
219 FloatToFloatS16(&out_interleaved[0], out_interleaved.size(),
220 &out_interleaved[0]);
221 out_file.WriteSamples(&out_interleaved[0], out_interleaved.size());
ekmeyerson60d9b332015-08-14 10:35:55 -0700222 if (process_reverse) {
223 Interleave(out_rev_buf->channels(), out_rev_buf->num_frames(),
224 out_rev_buf->num_channels(), out_rev_interleaved.data());
225 FloatToFloatS16(out_rev_interleaved.data(), out_rev_interleaved.size(),
226 out_rev_interleaved.data());
227 out_rev_file->WriteSamples(out_rev_interleaved.data(),
228 out_rev_interleaved.size());
229 }
Alejandro Luebs5d22c002015-04-15 11:26:40 -0700230 num_chunks++;
andrew@webrtc.org08df9b22014-12-16 20:57:15 +0000231 }
Alejandro Luebs5d22c002015-04-15 11:26:40 -0700232 if (FLAGS_perf) {
Andrew MacDonaldb444b3f2015-05-27 17:26:03 -0700233 int64_t execution_time_ms = accumulated_time.Milliseconds();
234 printf("\nExecution time: %.3f s\nFile time: %.2f s\n"
235 "Time per chunk: %.3f ms\n",
236 execution_time_ms * 0.001f, num_chunks * 1.f / kChunksPerSecond,
237 execution_time_ms * 1.f / num_chunks);
Alejandro Luebs5d22c002015-04-15 11:26:40 -0700238 }
andrew@webrtc.org08df9b22014-12-16 20:57:15 +0000239 return 0;
240}
241
242} // namespace webrtc
243
244int main(int argc, char* argv[]) {
245 return webrtc::main(argc, argv);
246}