blob: f22c41e2f84e79fc97a78a673d83e9af8987201e [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 };
ekmeyerson60d9b332015-08-14 10:35:55 -0700171 const StreamConfig reverse_input_config = {
172 in_rev_file->sample_rate(), in_rev_file->num_channels(),
173 };
174 const StreamConfig reverse_output_config = {
175 out_rev_file->sample_rate(), out_rev_file->num_channels(),
176 };
Andrew MacDonaldcb05b722015-05-07 22:17:51 -0700177 while (in_file.ReadSamples(in_interleaved.size(),
178 &in_interleaved[0]) == in_interleaved.size()) {
Andrew MacDonaldb444b3f2015-05-27 17:26:03 -0700179 // Have logs display the file time rather than wallclock time.
180 trace_to_stderr.SetTimeSeconds(num_chunks * 1.f / kChunksPerSecond);
Andrew MacDonaldcb05b722015-05-07 22:17:51 -0700181 FloatS16ToFloat(&in_interleaved[0], in_interleaved.size(),
182 &in_interleaved[0]);
183 Deinterleave(&in_interleaved[0], in_buf.num_frames(),
184 in_buf.num_channels(), in_buf.channels());
ekmeyerson60d9b332015-08-14 10:35:55 -0700185 if (process_reverse) {
186 in_rev_file->ReadSamples(in_rev_interleaved.size(),
187 in_rev_interleaved.data());
188 FloatS16ToFloat(in_rev_interleaved.data(), in_rev_interleaved.size(),
189 in_rev_interleaved.data());
190 Deinterleave(in_rev_interleaved.data(), in_rev_buf->num_frames(),
191 in_rev_buf->num_channels(), in_rev_buf->channels());
192 }
Andrew MacDonaldcb05b722015-05-07 22:17:51 -0700193
Alejandro Luebs5d22c002015-04-15 11:26:40 -0700194 if (FLAGS_perf) {
195 processing_start_time = TickTime::Now();
196 }
Michael Graczyk86c6d332015-07-23 11:41:39 -0700197 CHECK_EQ(kNoErr, ap->ProcessStream(in_buf.channels(), input_config,
198 output_config, out_buf.channels()));
ekmeyerson60d9b332015-08-14 10:35:55 -0700199 if (process_reverse) {
200 CHECK_EQ(kNoErr, ap->ProcessReverseStream(
201 in_rev_buf->channels(), reverse_input_config,
202 reverse_output_config, out_rev_buf->channels()));
203 }
Alejandro Luebs5d22c002015-04-15 11:26:40 -0700204 if (FLAGS_perf) {
205 accumulated_time += TickTime::Now() - processing_start_time;
206 }
Andrew MacDonaldcb05b722015-05-07 22:17:51 -0700207
208 Interleave(out_buf.channels(), out_buf.num_frames(),
209 out_buf.num_channels(), &out_interleaved[0]);
210 FloatToFloatS16(&out_interleaved[0], out_interleaved.size(),
211 &out_interleaved[0]);
212 out_file.WriteSamples(&out_interleaved[0], out_interleaved.size());
ekmeyerson60d9b332015-08-14 10:35:55 -0700213 if (process_reverse) {
214 Interleave(out_rev_buf->channels(), out_rev_buf->num_frames(),
215 out_rev_buf->num_channels(), out_rev_interleaved.data());
216 FloatToFloatS16(out_rev_interleaved.data(), out_rev_interleaved.size(),
217 out_rev_interleaved.data());
218 out_rev_file->WriteSamples(out_rev_interleaved.data(),
219 out_rev_interleaved.size());
220 }
Alejandro Luebs5d22c002015-04-15 11:26:40 -0700221 num_chunks++;
andrew@webrtc.org08df9b22014-12-16 20:57:15 +0000222 }
Alejandro Luebs5d22c002015-04-15 11:26:40 -0700223 if (FLAGS_perf) {
Andrew MacDonaldb444b3f2015-05-27 17:26:03 -0700224 int64_t execution_time_ms = accumulated_time.Milliseconds();
225 printf("\nExecution time: %.3f s\nFile time: %.2f s\n"
226 "Time per chunk: %.3f ms\n",
227 execution_time_ms * 0.001f, num_chunks * 1.f / kChunksPerSecond,
228 execution_time_ms * 1.f / num_chunks);
Alejandro Luebs5d22c002015-04-15 11:26:40 -0700229 }
andrew@webrtc.org08df9b22014-12-16 20:57:15 +0000230 return 0;
231}
232
233} // namespace webrtc
234
235int main(int argc, char* argv[]) {
236 return webrtc::main(argc, argv);
237}