blob: f4aab32acf4a3d55c8db1655f2accd37f303e3e9 [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
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);
108 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()) {
andrew@webrtc.org08df9b22014-12-16 20:57:15 +0000115 CHECK_EQ(kNoErr, ap->echo_cancellation()->Enable(FLAGS_aec || FLAGS_all));
116 } else if (FLAGS_aec) {
117 fprintf(stderr, "-aec requires a -dump file.\n");
118 return -1;
119 }
ekmeyerson60d9b332015-08-14 10:35:55 -0700120 bool process_reverse = !FLAGS_i_rev.empty();
andrew@webrtc.org08df9b22014-12-16 20:57:15 +0000121 CHECK_EQ(kNoErr, ap->gain_control()->Enable(FLAGS_agc || FLAGS_all));
122 CHECK_EQ(kNoErr, ap->gain_control()->set_mode(GainControl::kFixedDigital));
123 CHECK_EQ(kNoErr, ap->high_pass_filter()->Enable(FLAGS_hpf || FLAGS_all));
124 CHECK_EQ(kNoErr, ap->noise_suppression()->Enable(FLAGS_ns || FLAGS_all));
125 if (FLAGS_ns_level != -1)
126 CHECK_EQ(kNoErr, ap->noise_suppression()->set_level(
127 static_cast<NoiseSuppression::Level>(FLAGS_ns_level)));
128
mgraczyk@chromium.org5a92b782015-01-15 01:28:36 +0000129 printf("Input file: %s\nChannels: %d, Sample rate: %d Hz\n\n",
Andrew MacDonaldcb05b722015-05-07 22:17:51 -0700130 FLAGS_i.c_str(), in_file.num_channels(), in_file.sample_rate());
mgraczyk@chromium.org5a92b782015-01-15 01:28:36 +0000131 printf("Output file: %s\nChannels: %d, Sample rate: %d Hz\n\n",
Andrew MacDonaldcb05b722015-05-07 22:17:51 -0700132 FLAGS_o.c_str(), out_file.num_channels(), out_file.sample_rate());
mgraczyk@chromium.org5a92b782015-01-15 01:28:36 +0000133
Andrew MacDonaldcb05b722015-05-07 22:17:51 -0700134 ChannelBuffer<float> in_buf(
135 rtc::CheckedDivExact(in_file.sample_rate(), kChunksPerSecond),
136 in_file.num_channels());
137 ChannelBuffer<float> out_buf(
138 rtc::CheckedDivExact(out_file.sample_rate(), kChunksPerSecond),
139 out_file.num_channels());
andrew@webrtc.org08df9b22014-12-16 20:57:15 +0000140
Andrew MacDonaldcb05b722015-05-07 22:17:51 -0700141 std::vector<float> in_interleaved(in_buf.size());
142 std::vector<float> out_interleaved(out_buf.size());
ekmeyerson60d9b332015-08-14 10:35:55 -0700143
144 rtc::scoped_ptr<WavReader> in_rev_file;
145 rtc::scoped_ptr<WavWriter> out_rev_file;
146 rtc::scoped_ptr<ChannelBuffer<float>> in_rev_buf;
147 rtc::scoped_ptr<ChannelBuffer<float>> out_rev_buf;
148 std::vector<float> in_rev_interleaved;
149 std::vector<float> out_rev_interleaved;
150 if (process_reverse) {
151 in_rev_file.reset(new WavReader(FLAGS_i_rev));
152 out_rev_file.reset(new WavWriter(FLAGS_o_rev, in_rev_file->sample_rate(),
153 in_rev_file->num_channels()));
154 printf("In rev file: %s\nChannels: %d, Sample rate: %d Hz\n\n",
155 FLAGS_i_rev.c_str(), in_rev_file->num_channels(),
156 in_rev_file->sample_rate());
157 printf("Out rev file: %s\nChannels: %d, Sample rate: %d Hz\n\n",
158 FLAGS_o_rev.c_str(), out_rev_file->num_channels(),
159 out_rev_file->sample_rate());
160 in_rev_buf.reset(new ChannelBuffer<float>(
161 rtc::CheckedDivExact(in_rev_file->sample_rate(), kChunksPerSecond),
162 in_rev_file->num_channels()));
163 in_rev_interleaved.resize(in_rev_buf->size());
164 out_rev_buf.reset(new ChannelBuffer<float>(
165 rtc::CheckedDivExact(out_rev_file->sample_rate(), kChunksPerSecond),
166 out_rev_file->num_channels()));
167 out_rev_interleaved.resize(out_rev_buf->size());
168 }
169
Alejandro Luebs5d22c002015-04-15 11:26:40 -0700170 TickTime processing_start_time;
171 TickInterval accumulated_time;
172 int num_chunks = 0;
Michael Graczyk86c6d332015-07-23 11:41:39 -0700173
Andrew MacDonaldbc2296d2015-08-24 17:29:26 -0700174 const auto input_config = MakeStreamConfig(&in_file);
175 const auto output_config = MakeStreamConfig(&out_file);
176 const auto reverse_input_config = MakeStreamConfig(in_rev_file.get());
177 const auto reverse_output_config = MakeStreamConfig(out_rev_file.get());
Andrew MacDonaldc0775c02015-08-21 09:32:53 -0700178
Andrew MacDonaldcb05b722015-05-07 22:17:51 -0700179 while (in_file.ReadSamples(in_interleaved.size(),
180 &in_interleaved[0]) == in_interleaved.size()) {
Andrew MacDonaldb444b3f2015-05-27 17:26:03 -0700181 // Have logs display the file time rather than wallclock time.
182 trace_to_stderr.SetTimeSeconds(num_chunks * 1.f / kChunksPerSecond);
Andrew MacDonaldcb05b722015-05-07 22:17:51 -0700183 FloatS16ToFloat(&in_interleaved[0], in_interleaved.size(),
184 &in_interleaved[0]);
185 Deinterleave(&in_interleaved[0], in_buf.num_frames(),
186 in_buf.num_channels(), in_buf.channels());
ekmeyerson60d9b332015-08-14 10:35:55 -0700187 if (process_reverse) {
188 in_rev_file->ReadSamples(in_rev_interleaved.size(),
189 in_rev_interleaved.data());
190 FloatS16ToFloat(in_rev_interleaved.data(), in_rev_interleaved.size(),
191 in_rev_interleaved.data());
192 Deinterleave(in_rev_interleaved.data(), in_rev_buf->num_frames(),
193 in_rev_buf->num_channels(), in_rev_buf->channels());
194 }
Andrew MacDonaldcb05b722015-05-07 22:17:51 -0700195
Alejandro Luebs5d22c002015-04-15 11:26:40 -0700196 if (FLAGS_perf) {
197 processing_start_time = TickTime::Now();
198 }
Michael Graczyk86c6d332015-07-23 11:41:39 -0700199 CHECK_EQ(kNoErr, ap->ProcessStream(in_buf.channels(), input_config,
200 output_config, out_buf.channels()));
ekmeyerson60d9b332015-08-14 10:35:55 -0700201 if (process_reverse) {
202 CHECK_EQ(kNoErr, ap->ProcessReverseStream(
203 in_rev_buf->channels(), reverse_input_config,
204 reverse_output_config, out_rev_buf->channels()));
205 }
Alejandro Luebs5d22c002015-04-15 11:26:40 -0700206 if (FLAGS_perf) {
207 accumulated_time += TickTime::Now() - processing_start_time;
208 }
Andrew MacDonaldcb05b722015-05-07 22:17:51 -0700209
210 Interleave(out_buf.channels(), out_buf.num_frames(),
211 out_buf.num_channels(), &out_interleaved[0]);
212 FloatToFloatS16(&out_interleaved[0], out_interleaved.size(),
213 &out_interleaved[0]);
214 out_file.WriteSamples(&out_interleaved[0], out_interleaved.size());
ekmeyerson60d9b332015-08-14 10:35:55 -0700215 if (process_reverse) {
216 Interleave(out_rev_buf->channels(), out_rev_buf->num_frames(),
217 out_rev_buf->num_channels(), out_rev_interleaved.data());
218 FloatToFloatS16(out_rev_interleaved.data(), out_rev_interleaved.size(),
219 out_rev_interleaved.data());
220 out_rev_file->WriteSamples(out_rev_interleaved.data(),
221 out_rev_interleaved.size());
222 }
Alejandro Luebs5d22c002015-04-15 11:26:40 -0700223 num_chunks++;
andrew@webrtc.org08df9b22014-12-16 20:57:15 +0000224 }
Alejandro Luebs5d22c002015-04-15 11:26:40 -0700225 if (FLAGS_perf) {
Andrew MacDonaldb444b3f2015-05-27 17:26:03 -0700226 int64_t execution_time_ms = accumulated_time.Milliseconds();
227 printf("\nExecution time: %.3f s\nFile time: %.2f s\n"
228 "Time per chunk: %.3f ms\n",
229 execution_time_ms * 0.001f, num_chunks * 1.f / kChunksPerSecond,
230 execution_time_ms * 1.f / num_chunks);
Alejandro Luebs5d22c002015-04-15 11:26:40 -0700231 }
andrew@webrtc.org08df9b22014-12-16 20:57:15 +0000232 return 0;
233}
234
235} // namespace webrtc
236
237int main(int argc, char* argv[]) {
238 return webrtc::main(argc, argv);
239}