blob: 88d636e825c657d2491dffa7ab46059ee7f93a81 [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\"");
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
Andrew MacDonaldbc2296d2015-08-24 17:29:26 -070066// Returns a StreamConfig corresponding to wav_file if it's non-nullptr.
67// Otherwise returns a default initialized StreamConfig.
68StreamConfig MakeStreamConfig(const WavFile* wav_file) {
69 if (wav_file) {
70 return {wav_file->sample_rate(), wav_file->num_channels()};
71 }
72 return {};
73}
74
mgraczyk@chromium.org4ddde2e2015-01-29 22:39:44 +000075} // namespace
76
andrew@webrtc.org08df9b22014-12-16 20:57:15 +000077int main(int argc, char* argv[]) {
Andrew MacDonaldcb05b722015-05-07 22:17:51 -070078 google::SetUsageMessage(kUsage);
andrew@webrtc.org08df9b22014-12-16 20:57:15 +000079 google::ParseCommandLineFlags(&argc, &argv, true);
80
pbosbb36fdf2015-07-09 07:48:14 -070081 if (!((FLAGS_i.empty()) ^ (FLAGS_dump.empty()))) {
andrew@webrtc.org08df9b22014-12-16 20:57:15 +000082 fprintf(stderr,
Andrew MacDonaldcb05b722015-05-07 22:17:51 -070083 "An input file must be specified with either -i or -dump.\n");
andrew@webrtc.org08df9b22014-12-16 20:57:15 +000084 return 1;
85 }
pbosbb36fdf2015-07-09 07:48:14 -070086 if (!FLAGS_dump.empty()) {
andrew@webrtc.org08df9b22014-12-16 20:57:15 +000087 fprintf(stderr, "FIXME: the -dump option is not yet implemented.\n");
88 return 1;
89 }
90
Andrew MacDonaldb444b3f2015-05-27 17:26:03 -070091 test::TraceToStderr trace_to_stderr(true);
Andrew MacDonaldcb05b722015-05-07 22:17:51 -070092 WavReader in_file(FLAGS_i);
andrew@webrtc.org08df9b22014-12-16 20:57:15 +000093 // If the output format is uninitialized, use the input format.
Andrew MacDonaldcb05b722015-05-07 22:17:51 -070094 const int out_channels =
95 FLAGS_out_channels ? FLAGS_out_channels : in_file.num_channels();
96 const int out_sample_rate =
97 FLAGS_out_sample_rate ? FLAGS_out_sample_rate : in_file.sample_rate();
98 WavWriter out_file(FLAGS_o, out_sample_rate, out_channels);
andrew@webrtc.org08df9b22014-12-16 20:57:15 +000099
andrew@webrtc.org08df9b22014-12-16 20:57:15 +0000100 Config config;
101 config.Set<ExperimentalNs>(new ExperimentalNs(FLAGS_ts || FLAGS_all));
ekmeyerson60d9b332015-08-14 10:35:55 -0700102 config.Set<Intelligibility>(new Intelligibility(FLAGS_ie || FLAGS_all));
mgraczyk@chromium.org5a92b782015-01-15 01:28:36 +0000103
104 if (FLAGS_bf || FLAGS_all) {
Andrew MacDonaldcb05b722015-05-07 22:17:51 -0700105 const size_t num_mics = in_file.num_channels();
106 const std::vector<Point> array_geometry =
107 ParseArrayGeometry(FLAGS_mic_positions, num_mics);
henrikg91d6ede2015-09-17 00:24:34 -0700108 RTC_CHECK_EQ(array_geometry.size(), num_mics);
mgraczyk@chromium.org5a92b782015-01-15 01:28:36 +0000109
110 config.Set<Beamforming>(new Beamforming(true, array_geometry));
111 }
112
kwiberg@webrtc.org00b8f6b2015-02-26 14:34:55 +0000113 rtc::scoped_ptr<AudioProcessing> ap(AudioProcessing::Create(config));
pbosbb36fdf2015-07-09 07:48:14 -0700114 if (!FLAGS_dump.empty()) {
henrikg91d6ede2015-09-17 00:24:34 -0700115 RTC_CHECK_EQ(kNoErr,
116 ap->echo_cancellation()->Enable(FLAGS_aec || FLAGS_all));
andrew@webrtc.org08df9b22014-12-16 20:57:15 +0000117 } else if (FLAGS_aec) {
118 fprintf(stderr, "-aec requires a -dump file.\n");
119 return -1;
120 }
ekmeyerson60d9b332015-08-14 10:35:55 -0700121 bool process_reverse = !FLAGS_i_rev.empty();
henrikg91d6ede2015-09-17 00:24:34 -0700122 RTC_CHECK_EQ(kNoErr, ap->gain_control()->Enable(FLAGS_agc || FLAGS_all));
123 RTC_CHECK_EQ(kNoErr,
124 ap->gain_control()->set_mode(GainControl::kFixedDigital));
125 RTC_CHECK_EQ(kNoErr, ap->high_pass_filter()->Enable(FLAGS_hpf || FLAGS_all));
126 RTC_CHECK_EQ(kNoErr, ap->noise_suppression()->Enable(FLAGS_ns || FLAGS_all));
andrew@webrtc.org08df9b22014-12-16 20:57:15 +0000127 if (FLAGS_ns_level != -1)
henrikg91d6ede2015-09-17 00:24:34 -0700128 RTC_CHECK_EQ(kNoErr,
129 ap->noise_suppression()->set_level(
130 static_cast<NoiseSuppression::Level>(FLAGS_ns_level)));
andrew@webrtc.org08df9b22014-12-16 20:57:15 +0000131
mgraczyk@chromium.org5a92b782015-01-15 01:28:36 +0000132 printf("Input file: %s\nChannels: %d, Sample rate: %d Hz\n\n",
Andrew MacDonaldcb05b722015-05-07 22:17:51 -0700133 FLAGS_i.c_str(), in_file.num_channels(), in_file.sample_rate());
mgraczyk@chromium.org5a92b782015-01-15 01:28:36 +0000134 printf("Output file: %s\nChannels: %d, Sample rate: %d Hz\n\n",
Andrew MacDonaldcb05b722015-05-07 22:17:51 -0700135 FLAGS_o.c_str(), out_file.num_channels(), out_file.sample_rate());
mgraczyk@chromium.org5a92b782015-01-15 01:28:36 +0000136
Andrew MacDonaldcb05b722015-05-07 22:17:51 -0700137 ChannelBuffer<float> in_buf(
138 rtc::CheckedDivExact(in_file.sample_rate(), kChunksPerSecond),
139 in_file.num_channels());
140 ChannelBuffer<float> out_buf(
141 rtc::CheckedDivExact(out_file.sample_rate(), kChunksPerSecond),
142 out_file.num_channels());
andrew@webrtc.org08df9b22014-12-16 20:57:15 +0000143
Andrew MacDonaldcb05b722015-05-07 22:17:51 -0700144 std::vector<float> in_interleaved(in_buf.size());
145 std::vector<float> out_interleaved(out_buf.size());
ekmeyerson60d9b332015-08-14 10:35:55 -0700146
147 rtc::scoped_ptr<WavReader> in_rev_file;
148 rtc::scoped_ptr<WavWriter> out_rev_file;
149 rtc::scoped_ptr<ChannelBuffer<float>> in_rev_buf;
150 rtc::scoped_ptr<ChannelBuffer<float>> out_rev_buf;
151 std::vector<float> in_rev_interleaved;
152 std::vector<float> out_rev_interleaved;
153 if (process_reverse) {
154 in_rev_file.reset(new WavReader(FLAGS_i_rev));
155 out_rev_file.reset(new WavWriter(FLAGS_o_rev, in_rev_file->sample_rate(),
156 in_rev_file->num_channels()));
157 printf("In rev file: %s\nChannels: %d, Sample rate: %d Hz\n\n",
158 FLAGS_i_rev.c_str(), in_rev_file->num_channels(),
159 in_rev_file->sample_rate());
160 printf("Out rev file: %s\nChannels: %d, Sample rate: %d Hz\n\n",
161 FLAGS_o_rev.c_str(), out_rev_file->num_channels(),
162 out_rev_file->sample_rate());
163 in_rev_buf.reset(new ChannelBuffer<float>(
164 rtc::CheckedDivExact(in_rev_file->sample_rate(), kChunksPerSecond),
165 in_rev_file->num_channels()));
166 in_rev_interleaved.resize(in_rev_buf->size());
167 out_rev_buf.reset(new ChannelBuffer<float>(
168 rtc::CheckedDivExact(out_rev_file->sample_rate(), kChunksPerSecond),
169 out_rev_file->num_channels()));
170 out_rev_interleaved.resize(out_rev_buf->size());
171 }
172
Alejandro Luebs5d22c002015-04-15 11:26:40 -0700173 TickTime processing_start_time;
174 TickInterval accumulated_time;
175 int num_chunks = 0;
Michael Graczyk86c6d332015-07-23 11:41:39 -0700176
Andrew MacDonaldbc2296d2015-08-24 17:29:26 -0700177 const auto input_config = MakeStreamConfig(&in_file);
178 const auto output_config = MakeStreamConfig(&out_file);
179 const auto reverse_input_config = MakeStreamConfig(in_rev_file.get());
180 const auto reverse_output_config = MakeStreamConfig(out_rev_file.get());
Andrew MacDonaldc0775c02015-08-21 09:32:53 -0700181
Andrew MacDonaldcb05b722015-05-07 22:17:51 -0700182 while (in_file.ReadSamples(in_interleaved.size(),
183 &in_interleaved[0]) == in_interleaved.size()) {
Andrew MacDonaldb444b3f2015-05-27 17:26:03 -0700184 // Have logs display the file time rather than wallclock time.
185 trace_to_stderr.SetTimeSeconds(num_chunks * 1.f / kChunksPerSecond);
Andrew MacDonaldcb05b722015-05-07 22:17:51 -0700186 FloatS16ToFloat(&in_interleaved[0], in_interleaved.size(),
187 &in_interleaved[0]);
188 Deinterleave(&in_interleaved[0], in_buf.num_frames(),
189 in_buf.num_channels(), in_buf.channels());
ekmeyerson60d9b332015-08-14 10:35:55 -0700190 if (process_reverse) {
191 in_rev_file->ReadSamples(in_rev_interleaved.size(),
192 in_rev_interleaved.data());
193 FloatS16ToFloat(in_rev_interleaved.data(), in_rev_interleaved.size(),
194 in_rev_interleaved.data());
195 Deinterleave(in_rev_interleaved.data(), in_rev_buf->num_frames(),
196 in_rev_buf->num_channels(), in_rev_buf->channels());
197 }
Andrew MacDonaldcb05b722015-05-07 22:17:51 -0700198
Alejandro Luebs5d22c002015-04-15 11:26:40 -0700199 if (FLAGS_perf) {
200 processing_start_time = TickTime::Now();
201 }
henrikg91d6ede2015-09-17 00:24:34 -0700202 RTC_CHECK_EQ(kNoErr, ap->ProcessStream(in_buf.channels(), input_config,
203 output_config, out_buf.channels()));
ekmeyerson60d9b332015-08-14 10:35:55 -0700204 if (process_reverse) {
henrikg91d6ede2015-09-17 00:24:34 -0700205 RTC_CHECK_EQ(kNoErr, ap->ProcessReverseStream(
206 in_rev_buf->channels(), reverse_input_config,
207 reverse_output_config, out_rev_buf->channels()));
ekmeyerson60d9b332015-08-14 10:35:55 -0700208 }
Alejandro Luebs5d22c002015-04-15 11:26:40 -0700209 if (FLAGS_perf) {
210 accumulated_time += TickTime::Now() - processing_start_time;
211 }
Andrew MacDonaldcb05b722015-05-07 22:17:51 -0700212
213 Interleave(out_buf.channels(), out_buf.num_frames(),
214 out_buf.num_channels(), &out_interleaved[0]);
215 FloatToFloatS16(&out_interleaved[0], out_interleaved.size(),
216 &out_interleaved[0]);
217 out_file.WriteSamples(&out_interleaved[0], out_interleaved.size());
ekmeyerson60d9b332015-08-14 10:35:55 -0700218 if (process_reverse) {
219 Interleave(out_rev_buf->channels(), out_rev_buf->num_frames(),
220 out_rev_buf->num_channels(), out_rev_interleaved.data());
221 FloatToFloatS16(out_rev_interleaved.data(), out_rev_interleaved.size(),
222 out_rev_interleaved.data());
223 out_rev_file->WriteSamples(out_rev_interleaved.data(),
224 out_rev_interleaved.size());
225 }
Alejandro Luebs5d22c002015-04-15 11:26:40 -0700226 num_chunks++;
andrew@webrtc.org08df9b22014-12-16 20:57:15 +0000227 }
Alejandro Luebs5d22c002015-04-15 11:26:40 -0700228 if (FLAGS_perf) {
Andrew MacDonaldb444b3f2015-05-27 17:26:03 -0700229 int64_t execution_time_ms = accumulated_time.Milliseconds();
230 printf("\nExecution time: %.3f s\nFile time: %.2f s\n"
231 "Time per chunk: %.3f ms\n",
232 execution_time_ms * 0.001f, num_chunks * 1.f / kChunksPerSecond,
233 execution_time_ms * 1.f / num_chunks);
Alejandro Luebs5d22c002015-04-15 11:26:40 -0700234 }
andrew@webrtc.org08df9b22014-12-16 20:57:15 +0000235 return 0;
236}
237
238} // namespace webrtc
239
240int main(int argc, char* argv[]) {
241 return webrtc::main(argc, argv);
242}