blob: 2697e516026375c8a216656dd27cec6db8cdb8f2 [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
kjellanderb7a5c162015-11-05 12:33:18 -080026DEFINE_string(dump, "", "The name of the debug dump file to read from.");
27DEFINE_string(i, "", "The name of the input file to read from.");
28DEFINE_string(i_rev, "", "The name of the reverse input file to read from.");
29DEFINE_string(o, "out.wav", "Name of the output file to write to.");
30DEFINE_string(o_rev,
31 "out_rev.wav",
32 "Name of the reverse output file to write to.");
33DEFINE_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\"");
kjellanderb7a5c162015-11-05 12:33:18 -080040DEFINE_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
kjellanderb7a5c162015-11-05 12:33:18 -080067// 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 }
kjellanderb7a5c162015-11-05 12:33:18 -080087 if (!FLAGS_dump.empty()) {
88 fprintf(stderr, "FIXME: the -dump option is not yet implemented.\n");
andrew@webrtc.org08df9b22014-12-16 20:57:15 +000089 return 1;
90 }
91
Andrew MacDonaldb444b3f2015-05-27 17:26:03 -070092 test::TraceToStderr trace_to_stderr(true);
kjellanderb7a5c162015-11-05 12:33:18 -080093 WavReader in_file(FLAGS_i);
94 // If the output format is uninitialized, use the input format.
95 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);
100
andrew@webrtc.org08df9b22014-12-16 20:57:15 +0000101 Config config;
kjellander86b40502015-11-05 06:23:02 -0800102 config.Set<ExperimentalNs>(new ExperimentalNs(FLAGS_ts || FLAGS_all));
103 config.Set<Intelligibility>(new Intelligibility(FLAGS_ie || FLAGS_all));
kjellanderd2799412015-11-05 06:09:03 -0800104
kjellanderb7a5c162015-11-05 12:33:18 -0800105 if (FLAGS_bf || FLAGS_all) {
106 const size_t num_mics = in_file.num_channels();
107 const std::vector<Point> array_geometry =
108 ParseArrayGeometry(FLAGS_mic_positions, num_mics);
109 RTC_CHECK_EQ(array_geometry.size(), num_mics);
110
111 config.Set<Beamforming>(new Beamforming(
112 true, array_geometry,
113 SphericalPointf(DegreesToRadians(FLAGS_target_angle_degrees), 0.f,
114 1.f)));
115 }
116
kwiberg@webrtc.org00b8f6b2015-02-26 14:34:55 +0000117 rtc::scoped_ptr<AudioProcessing> ap(AudioProcessing::Create(config));
kjellanderb7a5c162015-11-05 12:33:18 -0800118 if (!FLAGS_dump.empty()) {
119 RTC_CHECK_EQ(kNoErr,
120 ap->echo_cancellation()->Enable(FLAGS_aec || FLAGS_all));
121 } else if (FLAGS_aec) {
122 fprintf(stderr, "-aec requires a -dump file.\n");
123 return -1;
124 }
125 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));
kjellanderb7a5c162015-11-05 12:33:18 -0800127 RTC_CHECK_EQ(kNoErr,
128 ap->gain_control()->set_mode(GainControl::kFixedDigital));
henrikg91d6ede2015-09-17 00:24:34 -0700129 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));
kjellander86b40502015-11-05 06:23:02 -0800131 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)));
ekmeyerson60d9b332015-08-14 10:35:55 -0700135 }
aluebs8e1809f2015-10-30 15:29:17 -0700136 ap->set_stream_key_pressed(FLAGS_ts);
ekmeyerson60d9b332015-08-14 10:35:55 -0700137
kjellanderb7a5c162015-11-05 12:33:18 -0800138 printf("Input file: %s\nChannels: %d, Sample rate: %d Hz\n\n",
139 FLAGS_i.c_str(), in_file.num_channels(), in_file.sample_rate());
140 printf("Output file: %s\nChannels: %d, Sample rate: %d Hz\n\n",
141 FLAGS_o.c_str(), out_file.num_channels(), out_file.sample_rate());
andrewbdafe312015-10-29 23:42:54 -0700142
kjellanderb7a5c162015-11-05 12:33:18 -0800143 ChannelBuffer<float> in_buf(
144 rtc::CheckedDivExact(in_file.sample_rate(), kChunksPerSecond),
145 in_file.num_channels());
146 ChannelBuffer<float> out_buf(
147 rtc::CheckedDivExact(out_file.sample_rate(), kChunksPerSecond),
148 out_file.num_channels());
149
150 std::vector<float> in_interleaved(in_buf.size());
151 std::vector<float> out_interleaved(out_buf.size());
152
153 rtc::scoped_ptr<WavReader> in_rev_file;
154 rtc::scoped_ptr<WavWriter> out_rev_file;
155 rtc::scoped_ptr<ChannelBuffer<float>> in_rev_buf;
156 rtc::scoped_ptr<ChannelBuffer<float>> out_rev_buf;
157 std::vector<float> in_rev_interleaved;
158 std::vector<float> out_rev_interleaved;
159 if (process_reverse) {
160 in_rev_file.reset(new WavReader(FLAGS_i_rev));
161 out_rev_file.reset(new WavWriter(FLAGS_o_rev, in_rev_file->sample_rate(),
162 in_rev_file->num_channels()));
163 printf("In rev file: %s\nChannels: %d, Sample rate: %d Hz\n\n",
164 FLAGS_i_rev.c_str(), in_rev_file->num_channels(),
165 in_rev_file->sample_rate());
166 printf("Out rev file: %s\nChannels: %d, Sample rate: %d Hz\n\n",
167 FLAGS_o_rev.c_str(), out_rev_file->num_channels(),
168 out_rev_file->sample_rate());
169 in_rev_buf.reset(new ChannelBuffer<float>(
170 rtc::CheckedDivExact(in_rev_file->sample_rate(), kChunksPerSecond),
171 in_rev_file->num_channels()));
172 in_rev_interleaved.resize(in_rev_buf->size());
173 out_rev_buf.reset(new ChannelBuffer<float>(
174 rtc::CheckedDivExact(out_rev_file->sample_rate(), kChunksPerSecond),
175 out_rev_file->num_channels()));
176 out_rev_interleaved.resize(out_rev_buf->size());
andrewbdafe312015-10-29 23:42:54 -0700177 }
178
kjellanderb7a5c162015-11-05 12:33:18 -0800179 TickTime processing_start_time;
180 TickInterval accumulated_time;
Alejandro Luebs5d22c002015-04-15 11:26:40 -0700181 int num_chunks = 0;
kjellanderb7a5c162015-11-05 12:33:18 -0800182
183 const auto input_config = MakeStreamConfig(&in_file);
184 const auto output_config = MakeStreamConfig(&out_file);
185 const auto reverse_input_config = MakeStreamConfig(in_rev_file.get());
186 const auto reverse_output_config = MakeStreamConfig(out_rev_file.get());
187
188 while (in_file.ReadSamples(in_interleaved.size(),
189 &in_interleaved[0]) == in_interleaved.size()) {
190 // Have logs display the file time rather than wallclock time.
Andrew MacDonaldb444b3f2015-05-27 17:26:03 -0700191 trace_to_stderr.SetTimeSeconds(num_chunks * 1.f / kChunksPerSecond);
kjellanderb7a5c162015-11-05 12:33:18 -0800192 FloatS16ToFloat(&in_interleaved[0], in_interleaved.size(),
193 &in_interleaved[0]);
194 Deinterleave(&in_interleaved[0], in_buf.num_frames(),
195 in_buf.num_channels(), in_buf.channels());
196 if (process_reverse) {
197 in_rev_file->ReadSamples(in_rev_interleaved.size(),
198 in_rev_interleaved.data());
199 FloatS16ToFloat(in_rev_interleaved.data(), in_rev_interleaved.size(),
200 in_rev_interleaved.data());
201 Deinterleave(in_rev_interleaved.data(), in_rev_buf->num_frames(),
202 in_rev_buf->num_channels(), in_rev_buf->channels());
203 }
kjellander86b40502015-11-05 06:23:02 -0800204
kjellanderb7a5c162015-11-05 12:33:18 -0800205 if (FLAGS_perf) {
206 processing_start_time = TickTime::Now();
207 }
208 RTC_CHECK_EQ(kNoErr, ap->ProcessStream(in_buf.channels(), input_config,
209 output_config, out_buf.channels()));
210 if (process_reverse) {
211 RTC_CHECK_EQ(kNoErr, ap->ProcessReverseStream(
212 in_rev_buf->channels(), reverse_input_config,
213 reverse_output_config, out_rev_buf->channels()));
214 }
215 if (FLAGS_perf) {
216 accumulated_time += TickTime::Now() - processing_start_time;
217 }
218
219 Interleave(out_buf.channels(), out_buf.num_frames(),
220 out_buf.num_channels(), &out_interleaved[0]);
221 FloatToFloatS16(&out_interleaved[0], out_interleaved.size(),
222 &out_interleaved[0]);
223 out_file.WriteSamples(&out_interleaved[0], out_interleaved.size());
224 if (process_reverse) {
225 Interleave(out_rev_buf->channels(), out_rev_buf->num_frames(),
226 out_rev_buf->num_channels(), out_rev_interleaved.data());
227 FloatToFloatS16(out_rev_interleaved.data(), out_rev_interleaved.size(),
228 out_rev_interleaved.data());
229 out_rev_file->WriteSamples(out_rev_interleaved.data(),
230 out_rev_interleaved.size());
231 }
232 num_chunks++;
233 }
Alejandro Luebs5d22c002015-04-15 11:26:40 -0700234 if (FLAGS_perf) {
kjellanderb7a5c162015-11-05 12:33:18 -0800235 int64_t execution_time_ms = accumulated_time.Milliseconds();
236 printf("\nExecution time: %.3f s\nFile time: %.2f s\n"
237 "Time per chunk: %.3f ms\n",
238 execution_time_ms * 0.001f, num_chunks * 1.f / kChunksPerSecond,
239 execution_time_ms * 1.f / num_chunks);
Alejandro Luebs5d22c002015-04-15 11:26:40 -0700240 }
andrew@webrtc.org08df9b22014-12-16 20:57:15 +0000241 return 0;
242}
243
244} // namespace webrtc
245
246int main(int argc, char* argv[]) {
247 return webrtc::main(argc, argv);
248}