andrew@webrtc.org | 08df9b2 | 2014-12-16 20:57:15 +0000 | [diff] [blame] | 1 | /* |
| 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.org | 4ddde2e | 2015-01-29 22:39:44 +0000 | [diff] [blame] | 12 | #include <sstream> |
mgraczyk@chromium.org | 5a92b78 | 2015-01-15 01:28:36 +0000 | [diff] [blame] | 13 | #include <string> |
andrew@webrtc.org | 08df9b2 | 2014-12-16 20:57:15 +0000 | [diff] [blame] | 14 | |
| 15 | #include "gflags/gflags.h" |
| 16 | #include "webrtc/base/checks.h" |
kwiberg@webrtc.org | 00b8f6b | 2015-02-26 14:34:55 +0000 | [diff] [blame] | 17 | #include "webrtc/base/scoped_ptr.h" |
kjellander@webrtc.org | 035e912 | 2015-01-28 19:57:00 +0000 | [diff] [blame] | 18 | #include "webrtc/common_audio/channel_buffer.h" |
andrew@webrtc.org | 08df9b2 | 2014-12-16 20:57:15 +0000 | [diff] [blame] | 19 | #include "webrtc/common_audio/wav_file.h" |
andrew@webrtc.org | 08df9b2 | 2014-12-16 20:57:15 +0000 | [diff] [blame] | 20 | #include "webrtc/modules/audio_processing/include/audio_processing.h" |
| 21 | #include "webrtc/modules/audio_processing/test/test_utils.h" |
Alejandro Luebs | 5d22c00 | 2015-04-15 11:26:40 -0700 | [diff] [blame^] | 22 | #include "webrtc/system_wrappers/interface/tick_util.h" |
andrew@webrtc.org | 08df9b2 | 2014-12-16 20:57:15 +0000 | [diff] [blame] | 23 | |
| 24 | DEFINE_string(dump, "", "The name of the debug dump file to read from."); |
| 25 | DEFINE_string(c, "", "The name of the capture input file to read from."); |
| 26 | DEFINE_string(o, "out.wav", "Name of the capture output file to write to."); |
| 27 | DEFINE_int32(o_channels, 0, "Number of output channels. Defaults to input."); |
| 28 | DEFINE_int32(o_sample_rate, 0, "Output sample rate in Hz. Defaults to input."); |
mgraczyk@chromium.org | 5a92b78 | 2015-01-15 01:28:36 +0000 | [diff] [blame] | 29 | DEFINE_double(mic_spacing, 0.0, |
mgraczyk@chromium.org | 4ddde2e | 2015-01-29 22:39:44 +0000 | [diff] [blame] | 30 | "Alternate way to specify mic_positions. " |
| 31 | "Assumes uniform linear array with specified spacings."); |
| 32 | DEFINE_string(mic_positions, "", |
| 33 | "Space delimited cartesian coordinates of microphones in meters. " |
| 34 | "The coordinates of each point are contiguous. " |
| 35 | "For a two element array: \"x1 y1 z1 x2 y2 z2\""); |
andrew@webrtc.org | 08df9b2 | 2014-12-16 20:57:15 +0000 | [diff] [blame] | 36 | |
| 37 | DEFINE_bool(aec, false, "Enable echo cancellation."); |
| 38 | DEFINE_bool(agc, false, "Enable automatic gain control."); |
| 39 | DEFINE_bool(hpf, false, "Enable high-pass filtering."); |
| 40 | DEFINE_bool(ns, false, "Enable noise suppression."); |
| 41 | DEFINE_bool(ts, false, "Enable transient suppression."); |
mgraczyk@chromium.org | 5a92b78 | 2015-01-15 01:28:36 +0000 | [diff] [blame] | 42 | DEFINE_bool(bf, false, "Enable beamforming."); |
andrew@webrtc.org | 08df9b2 | 2014-12-16 20:57:15 +0000 | [diff] [blame] | 43 | DEFINE_bool(all, false, "Enable all components."); |
| 44 | |
| 45 | DEFINE_int32(ns_level, -1, "Noise suppression level [0 - 3]."); |
| 46 | |
Alejandro Luebs | 5d22c00 | 2015-04-15 11:26:40 -0700 | [diff] [blame^] | 47 | DEFINE_bool(perf, false, "Enable performance tests."); |
| 48 | |
andrew@webrtc.org | 08df9b2 | 2014-12-16 20:57:15 +0000 | [diff] [blame] | 49 | static const int kChunksPerSecond = 100; |
| 50 | static const char kUsage[] = |
| 51 | "Command-line tool to run audio processing on WAV files. Accepts either\n" |
| 52 | "an input capture WAV file or protobuf debug dump and writes to an output\n" |
| 53 | "WAV file.\n" |
| 54 | "\n" |
| 55 | "All components are disabled by default. If any bi-directional components\n" |
| 56 | "are enabled, only debug dump files are permitted."; |
| 57 | |
| 58 | namespace webrtc { |
| 59 | |
mgraczyk@chromium.org | 4ddde2e | 2015-01-29 22:39:44 +0000 | [diff] [blame] | 60 | namespace { |
| 61 | |
| 62 | // Returns a vector<T> parsed from whitespace delimited values in to_parse, |
| 63 | // or an empty vector if the string could not be parsed. |
| 64 | template<typename T> |
| 65 | std::vector<T> parse_list(std::string to_parse) { |
| 66 | std::vector<T> values; |
| 67 | |
| 68 | std::istringstream str(to_parse); |
| 69 | std::copy( |
| 70 | std::istream_iterator<T>(str), |
| 71 | std::istream_iterator<T>(), |
| 72 | std::back_inserter(values)); |
| 73 | |
| 74 | return values; |
| 75 | } |
| 76 | |
| 77 | // Parses the array geometry from the command line. |
| 78 | // |
| 79 | // If a vector with size != num_mics is returned, an error has occurred and an |
| 80 | // appropriate error message has been printed to stdout. |
| 81 | std::vector<Point> get_array_geometry(size_t num_mics) { |
| 82 | std::vector<Point> result; |
| 83 | result.reserve(num_mics); |
| 84 | |
| 85 | if (FLAGS_mic_positions.length()) { |
| 86 | CHECK(FLAGS_mic_spacing == 0.0 && |
| 87 | "mic_positions and mic_spacing should not both be specified"); |
| 88 | |
| 89 | const std::vector<float> values = parse_list<float>(FLAGS_mic_positions); |
| 90 | if (values.size() != 3 * num_mics) { |
| 91 | fprintf(stderr, |
| 92 | "Could not parse mic_positions or incorrect number of points.\n"); |
| 93 | } else { |
| 94 | for (size_t i = 0; i < values.size(); i += 3) { |
| 95 | double x = values[i + 0]; |
| 96 | double y = values[i + 1]; |
| 97 | double z = values[i + 2]; |
| 98 | result.push_back(Point(x, y, z)); |
| 99 | } |
| 100 | } |
| 101 | } else { |
| 102 | if (FLAGS_mic_spacing <= 0) { |
| 103 | fprintf(stderr, |
| 104 | "mic_spacing must a positive value when beamforming is enabled.\n"); |
| 105 | } else { |
| 106 | for (size_t i = 0; i < num_mics; ++i) { |
aluebs@webrtc.org | 1d88394 | 2015-03-05 20:38:21 +0000 | [diff] [blame] | 107 | result.push_back(Point(i * FLAGS_mic_spacing, 0.f, 0.f)); |
mgraczyk@chromium.org | 4ddde2e | 2015-01-29 22:39:44 +0000 | [diff] [blame] | 108 | } |
| 109 | } |
| 110 | } |
| 111 | |
| 112 | return result; |
| 113 | } |
| 114 | |
| 115 | } // namespace |
| 116 | |
andrew@webrtc.org | 08df9b2 | 2014-12-16 20:57:15 +0000 | [diff] [blame] | 117 | int main(int argc, char* argv[]) { |
| 118 | { |
| 119 | const std::string program_name = argv[0]; |
| 120 | const std::string usage = kUsage; |
| 121 | google::SetUsageMessage(usage); |
| 122 | } |
| 123 | google::ParseCommandLineFlags(&argc, &argv, true); |
| 124 | |
| 125 | if (!((FLAGS_c == "") ^ (FLAGS_dump == ""))) { |
| 126 | fprintf(stderr, |
| 127 | "An input file must be specified with either -c or -dump.\n"); |
| 128 | return 1; |
| 129 | } |
| 130 | if (FLAGS_dump != "") { |
| 131 | fprintf(stderr, "FIXME: the -dump option is not yet implemented.\n"); |
| 132 | return 1; |
| 133 | } |
| 134 | |
| 135 | WavReader c_file(FLAGS_c); |
| 136 | // If the output format is uninitialized, use the input format. |
| 137 | int o_channels = FLAGS_o_channels; |
| 138 | if (!o_channels) |
| 139 | o_channels = c_file.num_channels(); |
| 140 | int o_sample_rate = FLAGS_o_sample_rate; |
| 141 | if (!o_sample_rate) |
| 142 | o_sample_rate = c_file.sample_rate(); |
| 143 | WavWriter o_file(FLAGS_o, o_sample_rate, o_channels); |
| 144 | |
andrew@webrtc.org | 08df9b2 | 2014-12-16 20:57:15 +0000 | [diff] [blame] | 145 | Config config; |
| 146 | config.Set<ExperimentalNs>(new ExperimentalNs(FLAGS_ts || FLAGS_all)); |
mgraczyk@chromium.org | 5a92b78 | 2015-01-15 01:28:36 +0000 | [diff] [blame] | 147 | |
| 148 | if (FLAGS_bf || FLAGS_all) { |
mgraczyk@chromium.org | 5a92b78 | 2015-01-15 01:28:36 +0000 | [diff] [blame] | 149 | const size_t num_mics = c_file.num_channels(); |
mgraczyk@chromium.org | 4ddde2e | 2015-01-29 22:39:44 +0000 | [diff] [blame] | 150 | const std::vector<Point> array_geometry = get_array_geometry(num_mics); |
| 151 | if (array_geometry.size() != num_mics) { |
| 152 | return 1; |
mgraczyk@chromium.org | 5a92b78 | 2015-01-15 01:28:36 +0000 | [diff] [blame] | 153 | } |
| 154 | |
| 155 | config.Set<Beamforming>(new Beamforming(true, array_geometry)); |
| 156 | } |
| 157 | |
kwiberg@webrtc.org | 00b8f6b | 2015-02-26 14:34:55 +0000 | [diff] [blame] | 158 | rtc::scoped_ptr<AudioProcessing> ap(AudioProcessing::Create(config)); |
andrew@webrtc.org | 08df9b2 | 2014-12-16 20:57:15 +0000 | [diff] [blame] | 159 | if (FLAGS_dump != "") { |
| 160 | CHECK_EQ(kNoErr, ap->echo_cancellation()->Enable(FLAGS_aec || FLAGS_all)); |
| 161 | } else if (FLAGS_aec) { |
| 162 | fprintf(stderr, "-aec requires a -dump file.\n"); |
| 163 | return -1; |
| 164 | } |
| 165 | CHECK_EQ(kNoErr, ap->gain_control()->Enable(FLAGS_agc || FLAGS_all)); |
| 166 | CHECK_EQ(kNoErr, ap->gain_control()->set_mode(GainControl::kFixedDigital)); |
| 167 | CHECK_EQ(kNoErr, ap->high_pass_filter()->Enable(FLAGS_hpf || FLAGS_all)); |
| 168 | CHECK_EQ(kNoErr, ap->noise_suppression()->Enable(FLAGS_ns || FLAGS_all)); |
| 169 | if (FLAGS_ns_level != -1) |
| 170 | CHECK_EQ(kNoErr, ap->noise_suppression()->set_level( |
| 171 | static_cast<NoiseSuppression::Level>(FLAGS_ns_level))); |
| 172 | |
mgraczyk@chromium.org | 5a92b78 | 2015-01-15 01:28:36 +0000 | [diff] [blame] | 173 | printf("Input file: %s\nChannels: %d, Sample rate: %d Hz\n\n", |
| 174 | FLAGS_c.c_str(), c_file.num_channels(), c_file.sample_rate()); |
| 175 | printf("Output file: %s\nChannels: %d, Sample rate: %d Hz\n\n", |
| 176 | FLAGS_o.c_str(), o_file.num_channels(), o_file.sample_rate()); |
| 177 | |
andrew@webrtc.org | 08df9b2 | 2014-12-16 20:57:15 +0000 | [diff] [blame] | 178 | ChannelBuffer<float> c_buf(c_file.sample_rate() / kChunksPerSecond, |
| 179 | c_file.num_channels()); |
| 180 | ChannelBuffer<float> o_buf(o_file.sample_rate() / kChunksPerSecond, |
| 181 | o_file.num_channels()); |
| 182 | |
aluebs@webrtc.org | d35a5c3 | 2015-02-10 22:52:15 +0000 | [diff] [blame] | 183 | const size_t c_length = |
| 184 | static_cast<size_t>(c_buf.num_channels() * c_buf.num_frames()); |
| 185 | const size_t o_length = |
| 186 | static_cast<size_t>(o_buf.num_channels() * o_buf.num_frames()); |
kwiberg@webrtc.org | 00b8f6b | 2015-02-26 14:34:55 +0000 | [diff] [blame] | 187 | rtc::scoped_ptr<float[]> c_interleaved(new float[c_length]); |
| 188 | rtc::scoped_ptr<float[]> o_interleaved(new float[o_length]); |
Alejandro Luebs | 5d22c00 | 2015-04-15 11:26:40 -0700 | [diff] [blame^] | 189 | TickTime processing_start_time; |
| 190 | TickInterval accumulated_time; |
| 191 | int num_chunks = 0; |
andrew@webrtc.org | 08df9b2 | 2014-12-16 20:57:15 +0000 | [diff] [blame] | 192 | while (c_file.ReadSamples(c_length, c_interleaved.get()) == c_length) { |
| 193 | FloatS16ToFloat(c_interleaved.get(), c_length, c_interleaved.get()); |
aluebs@webrtc.org | d35a5c3 | 2015-02-10 22:52:15 +0000 | [diff] [blame] | 194 | Deinterleave(c_interleaved.get(), c_buf.num_frames(), |
andrew@webrtc.org | 08df9b2 | 2014-12-16 20:57:15 +0000 | [diff] [blame] | 195 | c_buf.num_channels(), c_buf.channels()); |
Alejandro Luebs | 5d22c00 | 2015-04-15 11:26:40 -0700 | [diff] [blame^] | 196 | if (FLAGS_perf) { |
| 197 | processing_start_time = TickTime::Now(); |
| 198 | } |
andrew@webrtc.org | 08df9b2 | 2014-12-16 20:57:15 +0000 | [diff] [blame] | 199 | CHECK_EQ(kNoErr, |
| 200 | ap->ProcessStream(c_buf.channels(), |
aluebs@webrtc.org | d35a5c3 | 2015-02-10 22:52:15 +0000 | [diff] [blame] | 201 | c_buf.num_frames(), |
andrew@webrtc.org | 08df9b2 | 2014-12-16 20:57:15 +0000 | [diff] [blame] | 202 | c_file.sample_rate(), |
| 203 | LayoutFromChannels(c_buf.num_channels()), |
| 204 | o_file.sample_rate(), |
| 205 | LayoutFromChannels(o_buf.num_channels()), |
| 206 | o_buf.channels())); |
Alejandro Luebs | 5d22c00 | 2015-04-15 11:26:40 -0700 | [diff] [blame^] | 207 | if (FLAGS_perf) { |
| 208 | accumulated_time += TickTime::Now() - processing_start_time; |
| 209 | } |
aluebs@webrtc.org | d35a5c3 | 2015-02-10 22:52:15 +0000 | [diff] [blame] | 210 | Interleave(o_buf.channels(), o_buf.num_frames(), |
andrew@webrtc.org | 08df9b2 | 2014-12-16 20:57:15 +0000 | [diff] [blame] | 211 | o_buf.num_channels(), o_interleaved.get()); |
aluebs@webrtc.org | d35a5c3 | 2015-02-10 22:52:15 +0000 | [diff] [blame] | 212 | FloatToFloatS16(o_interleaved.get(), o_length, o_interleaved.get()); |
| 213 | o_file.WriteSamples(o_interleaved.get(), o_length); |
Alejandro Luebs | 5d22c00 | 2015-04-15 11:26:40 -0700 | [diff] [blame^] | 214 | num_chunks++; |
andrew@webrtc.org | 08df9b2 | 2014-12-16 20:57:15 +0000 | [diff] [blame] | 215 | } |
Alejandro Luebs | 5d22c00 | 2015-04-15 11:26:40 -0700 | [diff] [blame^] | 216 | if (FLAGS_perf) { |
| 217 | printf("Execution time: %.3fs\nFile time: %.2fs\n\n", |
| 218 | accumulated_time.Milliseconds() * 0.001f, num_chunks * 0.01f); |
| 219 | } |
andrew@webrtc.org | 08df9b2 | 2014-12-16 20:57:15 +0000 | [diff] [blame] | 220 | return 0; |
| 221 | } |
| 222 | |
| 223 | } // namespace webrtc |
| 224 | |
| 225 | int main(int argc, char* argv[]) { |
| 226 | return webrtc::main(argc, argv); |
| 227 | } |