Add WAV and arbitrary geometry support to nlbf test.
This adds functionality from audioproc_float. The geometry parsing code
is now shared from test_utils.h. I removed the "mic_spacing" flag from
audioproc_float because it's a redundancy that I suspect isn't very
useful.
Includes a cleanup of the audio_processing test utils. They're now
packaged in targets, with the protobuf-using ones split out to avoid
requiring users to depend on protobufs.
pcm_utils is no longer needed and removed.
The primary motivation for this CL is that AudioProcessing currently
doesn't support more than two channels and we'd like a way to pass
more channels to the beamformer.
R=aluebs@webrtc.org, mgraczyk@chromium.org
Review URL: https://webrtc-codereview.appspot.com/50899004
Cr-Commit-Position: refs/heads/master@{#9157}
diff --git a/webrtc/modules/audio_processing/audio_processing_tests.gypi b/webrtc/modules/audio_processing/audio_processing_tests.gypi
index a535144..f5d6dae 100644
--- a/webrtc/modules/audio_processing/audio_processing_tests.gypi
+++ b/webrtc/modules/audio_processing/audio_processing_tests.gypi
@@ -9,6 +9,18 @@
{
'targets': [
{
+ 'target_name': 'audioproc_test_utils',
+ 'type': 'static_library',
+ 'dependencies': [
+ '<(webrtc_root)/base/base.gyp:rtc_base_approved',
+ '<(webrtc_root)/common_audio/common_audio.gyp:common_audio',
+ ],
+ 'sources': [
+ 'test/test_utils.cc',
+ 'test/test_utils.h',
+ ],
+ },
+ {
'target_name': 'transient_suppression_test',
'type': 'executable',
'dependencies': [
@@ -39,13 +51,12 @@
'target_name': 'nonlinear_beamformer_test',
'type': 'executable',
'dependencies': [
+ 'audioproc_test_utils',
'<(DEPTH)/third_party/gflags/gflags.gyp:gflags',
'<(webrtc_root)/modules/modules.gyp:audio_processing',
],
'sources': [
'beamformer/nonlinear_beamformer_test.cc',
- 'beamformer/pcm_utils.cc',
- 'beamformer/pcm_utils.h',
],
}, # nonlinear_beamformer_test
],
@@ -66,11 +77,24 @@
'includes': [ '../../build/protoc.gypi', ],
},
{
+ 'target_name': 'audioproc_protobuf_utils',
+ 'type': 'static_library',
+ 'dependencies': [
+ 'audioproc_debug_proto',
+ ],
+ 'sources': [
+ 'test/protobuf_utils.cc',
+ 'test/protobuf_utils.h',
+ ],
+ },
+ {
'target_name': 'audioproc',
'type': 'executable',
'dependencies': [
'audio_processing',
'audioproc_debug_proto',
+ 'audioproc_test_utils',
+ 'audioproc_protobuf_utils',
'<(DEPTH)/testing/gtest.gyp:gtest',
'<(webrtc_root)/system_wrappers/system_wrappers.gyp:system_wrappers',
'<(webrtc_root)/test/test.gyp:test_support',
@@ -83,6 +107,8 @@
'dependencies': [
'audio_processing',
'audioproc_debug_proto',
+ 'audioproc_test_utils',
+ 'audioproc_protobuf_utils',
'<(DEPTH)/third_party/gflags/gflags.gyp:gflags',
],
'sources': [ 'test/audioproc_float.cc', ],
@@ -92,6 +118,8 @@
'type': 'executable',
'dependencies': [
'audioproc_debug_proto',
+ 'audioproc_test_utils',
+ 'audioproc_protobuf_utils',
'<(webrtc_root)/system_wrappers/system_wrappers.gyp:system_wrappers',
'<(webrtc_root)/common_audio/common_audio.gyp:common_audio',
'<(DEPTH)/third_party/gflags/gflags.gyp:gflags',
diff --git a/webrtc/modules/audio_processing/beamformer/nonlinear_beamformer.cc b/webrtc/modules/audio_processing/beamformer/nonlinear_beamformer.cc
index 8fd6c68..a99d3b1 100644
--- a/webrtc/modules/audio_processing/beamformer/nonlinear_beamformer.cc
+++ b/webrtc/modules/audio_processing/beamformer/nonlinear_beamformer.cc
@@ -294,7 +294,7 @@
}
void NonlinearBeamformer::ProcessChunk(const ChannelBuffer<float>& input,
- ChannelBuffer<float>* output) {
+ ChannelBuffer<float>* output) {
DCHECK_EQ(input.num_channels(), num_input_channels_);
DCHECK_EQ(input.num_frames_per_band(), chunk_length_);
@@ -324,10 +324,10 @@
}
void NonlinearBeamformer::ProcessAudioBlock(const complex_f* const* input,
- int num_input_channels,
- int num_freq_bins,
- int num_output_channels,
- complex_f* const* output) {
+ int num_input_channels,
+ int num_freq_bins,
+ int num_output_channels,
+ complex_f* const* output) {
CHECK_EQ(num_freq_bins, kNumFreqBins);
CHECK_EQ(num_input_channels, num_input_channels_);
CHECK_EQ(num_output_channels, 1);
@@ -398,7 +398,7 @@
}
void NonlinearBeamformer::ApplyMasks(const complex_f* const* input,
- complex_f* const* output) {
+ complex_f* const* output) {
complex_f* output_channel = output[0];
for (int f_ix = 0; f_ix < kNumFreqBins; ++f_ix) {
output_channel[f_ix] = complex_f(0.f, 0.f);
diff --git a/webrtc/modules/audio_processing/beamformer/nonlinear_beamformer_test.cc b/webrtc/modules/audio_processing/beamformer/nonlinear_beamformer_test.cc
index 48d7c2b..82a6cb0 100644
--- a/webrtc/modules/audio_processing/beamformer/nonlinear_beamformer_test.cc
+++ b/webrtc/modules/audio_processing/beamformer/nonlinear_beamformer_test.cc
@@ -8,75 +8,82 @@
* be found in the AUTHORS file in the root of the source tree.
*/
-#include <iostream>
#include <vector>
#include "gflags/gflags.h"
+#include "webrtc/base/checks.h"
+#include "webrtc/common_audio/channel_buffer.h"
+#include "webrtc/common_audio/wav_file.h"
#include "webrtc/modules/audio_processing/beamformer/nonlinear_beamformer.h"
-#include "webrtc/modules/audio_processing/beamformer/pcm_utils.h"
+#include "webrtc/modules/audio_processing/test/test_utils.h"
-DEFINE_int32(sample_rate,
- 48000,
- "The sample rate of the input file. The output"
- "file will be of the same sample rate.");
-DEFINE_int32(num_input_channels,
- 2,
- "The number of channels in the input file.");
-DEFINE_double(mic_spacing,
- 0.05,
- "The spacing between microphones on the chromebook which "
- "recorded the input file.");
-DEFINE_string(input_file_path,
- "input.wav",
- "The absolute path to the input file.");
-DEFINE_string(output_file_path,
- "beamformer_test_output.wav",
- "The absolute path to the output file.");
+DEFINE_string(i, "", "The name of the input file to read from.");
+DEFINE_string(o, "out.wav", "Name of the output file to write to.");
+DEFINE_string(mic_positions, "",
+ "Space delimited cartesian coordinates of microphones in meters. "
+ "The coordinates of each point are contiguous. "
+ "For a two element array: \"x1 y1 z1 x2 y2 z2\"");
-using webrtc::ChannelBuffer;
+namespace webrtc {
+namespace {
+
+const int kChunksPerSecond = 100;
+const int kChunkSizeMs = 1000 / kChunksPerSecond;
+
+const char kUsage[] =
+ "Command-line tool to run beamforming on WAV files. The signal is passed\n"
+ "in as a single band, unlike the audio processing interface which splits\n"
+ "signals into multiple bands.";
+
+} // namespace
int main(int argc, char* argv[]) {
+ google::SetUsageMessage(kUsage);
google::ParseCommandLineFlags(&argc, &argv, true);
- const float kChunkTimeMilliseconds = 10;
- const int kChunkSize = FLAGS_sample_rate / (1000.f / kChunkTimeMilliseconds);
- const int kInputSamplesPerChunk = kChunkSize * FLAGS_num_input_channels;
+ WavReader in_file(FLAGS_i);
+ WavWriter out_file(FLAGS_o, in_file.sample_rate(), 1);
- ChannelBuffer<float> captured_audio_cb(kChunkSize, FLAGS_num_input_channels);
+ const size_t num_mics = in_file.num_channels();
+ const std::vector<Point> array_geometry =
+ ParseArrayGeometry(FLAGS_mic_positions, num_mics);
+ CHECK_EQ(array_geometry.size(), num_mics);
- FILE* read_file = fopen(FLAGS_input_file_path.c_str(), "rb");
- if (!read_file) {
- std::cerr << "Input file '" << FLAGS_input_file_path << "' not found."
- << std::endl;
- return -1;
+ NonlinearBeamformer bf(array_geometry);
+ bf.Initialize(kChunkSizeMs, in_file.sample_rate());
+
+ printf("Input file: %s\nChannels: %d, Sample rate: %d Hz\n\n",
+ FLAGS_i.c_str(), in_file.num_channels(), in_file.sample_rate());
+ printf("Output file: %s\nChannels: %d, Sample rate: %d Hz\n\n",
+ FLAGS_o.c_str(), out_file.num_channels(), out_file.sample_rate());
+
+ ChannelBuffer<float> in_buf(
+ rtc::CheckedDivExact(in_file.sample_rate(), kChunksPerSecond),
+ in_file.num_channels());
+ ChannelBuffer<float> out_buf(
+ rtc::CheckedDivExact(out_file.sample_rate(), kChunksPerSecond),
+ out_file.num_channels());
+
+ std::vector<float> interleaved(in_buf.size());
+ while (in_file.ReadSamples(interleaved.size(),
+ &interleaved[0]) == interleaved.size()) {
+ FloatS16ToFloat(&interleaved[0], interleaved.size(), &interleaved[0]);
+ Deinterleave(&interleaved[0], in_buf.num_frames(),
+ in_buf.num_channels(), in_buf.channels());
+
+ bf.ProcessChunk(in_buf, &out_buf);
+
+ Interleave(out_buf.channels(), out_buf.num_frames(),
+ out_buf.num_channels(), &interleaved[0]);
+ FloatToFloatS16(&interleaved[0], interleaved.size(), &interleaved[0]);
+ out_file.WriteSamples(&interleaved[0], interleaved.size());
}
- // Skipping the .wav header. TODO: Add .wav header parsing.
- fseek(read_file, 44, SEEK_SET);
-
- FILE* write_file = fopen(FLAGS_output_file_path.c_str(), "wb");
-
- std::vector<webrtc::Point> array_geometry;
- for (int i = 0; i < FLAGS_num_input_channels; ++i) {
- array_geometry.push_back(webrtc::Point(i * FLAGS_mic_spacing, 0.f, 0.f));
- }
- webrtc::NonlinearBeamformer bf(array_geometry);
- bf.Initialize(kChunkTimeMilliseconds, FLAGS_sample_rate);
- while (true) {
- size_t samples_read = webrtc::PcmReadToFloat(read_file,
- kInputSamplesPerChunk,
- FLAGS_num_input_channels,
- captured_audio_cb.channels());
-
- if (static_cast<int>(samples_read) != kInputSamplesPerChunk) {
- break;
- }
-
- bf.ProcessChunk(captured_audio_cb, &captured_audio_cb);
- webrtc::PcmWriteFromFloat(
- write_file, kChunkSize, 1, captured_audio_cb.channels());
- }
- fclose(read_file);
- fclose(write_file);
return 0;
}
+
+} // namespace webrtc
+
+int main(int argc, char* argv[]) {
+ return webrtc::main(argc, argv);
+}
diff --git a/webrtc/modules/audio_processing/beamformer/pcm_utils.cc b/webrtc/modules/audio_processing/beamformer/pcm_utils.cc
deleted file mode 100644
index 0999de3..0000000
--- a/webrtc/modules/audio_processing/beamformer/pcm_utils.cc
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
- *
- * Use of this source code is governed by a BSD-style license
- * that can be found in the LICENSE file in the root of the source
- * tree. An additional intellectual property rights grant can be found
- * in the file PATENTS. All contributing project authors may
- * be found in the AUTHORS file in the root of the source tree.
- */
-
-#include "webrtc/modules/audio_processing/beamformer/pcm_utils.h"
-
-#include "webrtc/base/checks.h"
-#include "webrtc/common_audio/include/audio_util.h"
-#include "webrtc/common_audio/channel_buffer.h"
-
-namespace webrtc {
-
-size_t PcmRead(FILE* file,
- size_t length,
- int num_channels,
- int16_t* const* buffer) {
- CHECK_GE(num_channels, 1);
-
- rtc::scoped_ptr<int16_t[]> interleaved_buffer(new int16_t[length]);
- size_t elements_read = fread(interleaved_buffer.get(), sizeof(int16_t),
- length, file);
- if (elements_read != length) {
- // This is only an error if we haven't reached the end of the file.
- CHECK_NE(0, feof(file));
- }
-
- Deinterleave(interleaved_buffer.get(),
- static_cast<int>(elements_read) / num_channels,
- num_channels,
- buffer);
- return elements_read;
-}
-
-size_t PcmReadToFloat(FILE* file,
- size_t length,
- int num_channels,
- float* const* buffer) {
- CHECK_GE(num_channels, 1);
-
- int num_frames = static_cast<int>(length) / num_channels;
- rtc::scoped_ptr<ChannelBuffer<int16_t> > deinterleaved_buffer(
- new ChannelBuffer<int16_t>(num_frames, num_channels));
-
- size_t elements_read =
- PcmRead(file, length, num_channels, deinterleaved_buffer->channels());
-
- for (int i = 0; i < num_channels; ++i) {
- S16ToFloat(deinterleaved_buffer->channels()[i], num_frames, buffer[i]);
- }
- return elements_read;
-}
-
-void PcmWrite(FILE* file,
- size_t length,
- int num_channels,
- const int16_t* const* buffer) {
- CHECK_GE(num_channels, 1);
-
- rtc::scoped_ptr<int16_t[]> interleaved_buffer(new int16_t[length]);
- Interleave(buffer,
- static_cast<int>(length) / num_channels,
- num_channels,
- interleaved_buffer.get());
- CHECK_EQ(length,
- fwrite(interleaved_buffer.get(), sizeof(int16_t), length, file));
-}
-
-void PcmWriteFromFloat(FILE* file,
- size_t length,
- int num_channels,
- const float* const* buffer) {
- CHECK_GE(num_channels, 1);
-
- int num_frames = static_cast<int>(length) / num_channels;
- rtc::scoped_ptr<ChannelBuffer<int16_t> > deinterleaved_buffer(
- new ChannelBuffer<int16_t>(num_frames, num_channels));
-
- for (int i = 0; i < num_channels; ++i) {
- FloatToS16(buffer[i], num_frames, deinterleaved_buffer->channels()[i]);
- }
- PcmWrite(file, length, num_channels, deinterleaved_buffer->channels());
-}
-
-} // namespace webrtc
diff --git a/webrtc/modules/audio_processing/beamformer/pcm_utils.h b/webrtc/modules/audio_processing/beamformer/pcm_utils.h
deleted file mode 100644
index 3a6a3b9..0000000
--- a/webrtc/modules/audio_processing/beamformer/pcm_utils.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
- *
- * Use of this source code is governed by a BSD-style license
- * that can be found in the LICENSE file in the root of the source
- * tree. An additional intellectual property rights grant can be found
- * in the file PATENTS. All contributing project authors may
- * be found in the AUTHORS file in the root of the source tree.
- */
-
-#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_BEAMFORMER_PCM_UTILS_H_
-#define WEBRTC_MODULES_AUDIO_PROCESSING_BEAMFORMER_PCM_UTILS_H_
-
-#include <inttypes.h>
-#include <stdio.h>
-
-// Utilities for reading from and writing to multichannel pcm files.
-// Assumes a bit depth of 16 and little-endian. Note that in these functions,
-// length refers to the number of samples to read from/write to the file,
-// such that length / num_channels is the number of frames.
-namespace webrtc {
-
-// Reads audio from a pcm into a 2D array: buffer[channel_index][frame_index].
-// Returns the number of frames written. If this is less than |length|, it's
-// safe to assume the end-of-file was reached, as otherwise this will crash.
-// In PcmReadToFloat, the floats are within the range [-1, 1].
-size_t PcmRead(FILE* file,
- size_t length,
- int num_channels,
- int16_t* const* buffer);
-size_t PcmReadToFloat(FILE* file,
- size_t length,
- int num_channels,
- float* const* buffer);
-
-// Writes to a pcm file. The resulting file contains the channels interleaved.
-// Crashes if the correct number of frames aren't written to the file. For
-// PcmWriteFromFloat, floats must be within the range [-1, 1].
-void PcmWrite(FILE* file,
- size_t length,
- int num_channels,
- const int16_t* const* buffer);
-void PcmWriteFromFloat(FILE* file,
- size_t length,
- int num_channels,
- const float* const* buffer);
-
-} // namespace webrtc
-
-#endif // WEBRTC_MODULES_AUDIO_PROCESSING_BEAMFORMER_PCM_UTILS_H_
diff --git a/webrtc/modules/audio_processing/test/audio_processing_unittest.cc b/webrtc/modules/audio_processing/test/audio_processing_unittest.cc
index 6546cfa..e5c3865 100644
--- a/webrtc/modules/audio_processing/test/audio_processing_unittest.cc
+++ b/webrtc/modules/audio_processing/test/audio_processing_unittest.cc
@@ -22,6 +22,7 @@
#include "webrtc/modules/audio_processing/beamformer/mock_nonlinear_beamformer.h"
#include "webrtc/modules/audio_processing/common.h"
#include "webrtc/modules/audio_processing/include/audio_processing.h"
+#include "webrtc/modules/audio_processing/test/protobuf_utils.h"
#include "webrtc/modules/audio_processing/test/test_utils.h"
#include "webrtc/modules/interface/module_common_types.h"
#include "webrtc/system_wrappers/interface/event_wrapper.h"
diff --git a/webrtc/modules/audio_processing/test/audioproc_float.cc b/webrtc/modules/audio_processing/test/audioproc_float.cc
index 6278a21..b3765c5 100644
--- a/webrtc/modules/audio_processing/test/audioproc_float.cc
+++ b/webrtc/modules/audio_processing/test/audioproc_float.cc
@@ -18,17 +18,16 @@
#include "webrtc/common_audio/channel_buffer.h"
#include "webrtc/common_audio/wav_file.h"
#include "webrtc/modules/audio_processing/include/audio_processing.h"
+#include "webrtc/modules/audio_processing/test/protobuf_utils.h"
#include "webrtc/modules/audio_processing/test/test_utils.h"
#include "webrtc/system_wrappers/interface/tick_util.h"
DEFINE_string(dump, "", "The name of the debug dump file to read from.");
-DEFINE_string(c, "", "The name of the capture input file to read from.");
-DEFINE_string(o, "out.wav", "Name of the capture output file to write to.");
-DEFINE_int32(o_channels, 0, "Number of output channels. Defaults to input.");
-DEFINE_int32(o_sample_rate, 0, "Output sample rate in Hz. Defaults to input.");
-DEFINE_double(mic_spacing, 0.0,
- "Alternate way to specify mic_positions. "
- "Assumes uniform linear array with specified spacings.");
+DEFINE_string(i, "", "The name of the input file to read from.");
+DEFINE_string(o, "out.wav", "Name of the output file to write to.");
+DEFINE_int32(out_channels, 0, "Number of output channels. Defaults to input.");
+DEFINE_int32(out_sample_rate, 0,
+ "Output sample rate in Hz. Defaults to input.");
DEFINE_string(mic_positions, "",
"Space delimited cartesian coordinates of microphones in meters. "
"The coordinates of each point are contiguous. "
@@ -46,8 +45,11 @@
DEFINE_bool(perf, false, "Enable performance tests.");
-static const int kChunksPerSecond = 100;
-static const char kUsage[] =
+namespace webrtc {
+namespace {
+
+const int kChunksPerSecond = 100;
+const char kUsage[] =
"Command-line tool to run audio processing on WAV files. Accepts either\n"
"an input capture WAV file or protobuf debug dump and writes to an output\n"
"WAV file.\n"
@@ -55,76 +57,15 @@
"All components are disabled by default. If any bi-directional components\n"
"are enabled, only debug dump files are permitted.";
-namespace webrtc {
-
-namespace {
-
-// Returns a vector<T> parsed from whitespace delimited values in to_parse,
-// or an empty vector if the string could not be parsed.
-template<typename T>
-std::vector<T> parse_list(std::string to_parse) {
- std::vector<T> values;
-
- std::istringstream str(to_parse);
- std::copy(
- std::istream_iterator<T>(str),
- std::istream_iterator<T>(),
- std::back_inserter(values));
-
- return values;
-}
-
-// Parses the array geometry from the command line.
-//
-// If a vector with size != num_mics is returned, an error has occurred and an
-// appropriate error message has been printed to stdout.
-std::vector<Point> get_array_geometry(size_t num_mics) {
- std::vector<Point> result;
- result.reserve(num_mics);
-
- if (FLAGS_mic_positions.length()) {
- CHECK(FLAGS_mic_spacing == 0.0 &&
- "mic_positions and mic_spacing should not both be specified");
-
- const std::vector<float> values = parse_list<float>(FLAGS_mic_positions);
- if (values.size() != 3 * num_mics) {
- fprintf(stderr,
- "Could not parse mic_positions or incorrect number of points.\n");
- } else {
- for (size_t i = 0; i < values.size(); i += 3) {
- double x = values[i + 0];
- double y = values[i + 1];
- double z = values[i + 2];
- result.push_back(Point(x, y, z));
- }
- }
- } else {
- if (FLAGS_mic_spacing <= 0) {
- fprintf(stderr,
- "mic_spacing must a positive value when beamforming is enabled.\n");
- } else {
- for (size_t i = 0; i < num_mics; ++i) {
- result.push_back(Point(i * FLAGS_mic_spacing, 0.f, 0.f));
- }
- }
- }
-
- return result;
-}
-
} // namespace
int main(int argc, char* argv[]) {
- {
- const std::string program_name = argv[0];
- const std::string usage = kUsage;
- google::SetUsageMessage(usage);
- }
+ google::SetUsageMessage(kUsage);
google::ParseCommandLineFlags(&argc, &argv, true);
- if (!((FLAGS_c == "") ^ (FLAGS_dump == ""))) {
+ if (!((FLAGS_i == "") ^ (FLAGS_dump == ""))) {
fprintf(stderr,
- "An input file must be specified with either -c or -dump.\n");
+ "An input file must be specified with either -i or -dump.\n");
return 1;
}
if (FLAGS_dump != "") {
@@ -132,25 +73,22 @@
return 1;
}
- WavReader c_file(FLAGS_c);
+ WavReader in_file(FLAGS_i);
// If the output format is uninitialized, use the input format.
- int o_channels = FLAGS_o_channels;
- if (!o_channels)
- o_channels = c_file.num_channels();
- int o_sample_rate = FLAGS_o_sample_rate;
- if (!o_sample_rate)
- o_sample_rate = c_file.sample_rate();
- WavWriter o_file(FLAGS_o, o_sample_rate, o_channels);
+ const int out_channels =
+ FLAGS_out_channels ? FLAGS_out_channels : in_file.num_channels();
+ const int out_sample_rate =
+ FLAGS_out_sample_rate ? FLAGS_out_sample_rate : in_file.sample_rate();
+ WavWriter out_file(FLAGS_o, out_sample_rate, out_channels);
Config config;
config.Set<ExperimentalNs>(new ExperimentalNs(FLAGS_ts || FLAGS_all));
if (FLAGS_bf || FLAGS_all) {
- const size_t num_mics = c_file.num_channels();
- const std::vector<Point> array_geometry = get_array_geometry(num_mics);
- if (array_geometry.size() != num_mics) {
- return 1;
- }
+ const size_t num_mics = in_file.num_channels();
+ const std::vector<Point> array_geometry =
+ ParseArrayGeometry(FLAGS_mic_positions, num_mics);
+ CHECK_EQ(array_geometry.size(), num_mics);
config.Set<Beamforming>(new Beamforming(true, array_geometry));
}
@@ -171,46 +109,49 @@
static_cast<NoiseSuppression::Level>(FLAGS_ns_level)));
printf("Input file: %s\nChannels: %d, Sample rate: %d Hz\n\n",
- FLAGS_c.c_str(), c_file.num_channels(), c_file.sample_rate());
+ FLAGS_i.c_str(), in_file.num_channels(), in_file.sample_rate());
printf("Output file: %s\nChannels: %d, Sample rate: %d Hz\n\n",
- FLAGS_o.c_str(), o_file.num_channels(), o_file.sample_rate());
+ FLAGS_o.c_str(), out_file.num_channels(), out_file.sample_rate());
- ChannelBuffer<float> c_buf(c_file.sample_rate() / kChunksPerSecond,
- c_file.num_channels());
- ChannelBuffer<float> o_buf(o_file.sample_rate() / kChunksPerSecond,
- o_file.num_channels());
+ ChannelBuffer<float> in_buf(
+ rtc::CheckedDivExact(in_file.sample_rate(), kChunksPerSecond),
+ in_file.num_channels());
+ ChannelBuffer<float> out_buf(
+ rtc::CheckedDivExact(out_file.sample_rate(), kChunksPerSecond),
+ out_file.num_channels());
- const size_t c_length =
- static_cast<size_t>(c_buf.num_channels() * c_buf.num_frames());
- const size_t o_length =
- static_cast<size_t>(o_buf.num_channels() * o_buf.num_frames());
- rtc::scoped_ptr<float[]> c_interleaved(new float[c_length]);
- rtc::scoped_ptr<float[]> o_interleaved(new float[o_length]);
+ std::vector<float> in_interleaved(in_buf.size());
+ std::vector<float> out_interleaved(out_buf.size());
TickTime processing_start_time;
TickInterval accumulated_time;
int num_chunks = 0;
- while (c_file.ReadSamples(c_length, c_interleaved.get()) == c_length) {
- FloatS16ToFloat(c_interleaved.get(), c_length, c_interleaved.get());
- Deinterleave(c_interleaved.get(), c_buf.num_frames(),
- c_buf.num_channels(), c_buf.channels());
+ while (in_file.ReadSamples(in_interleaved.size(),
+ &in_interleaved[0]) == in_interleaved.size()) {
+ FloatS16ToFloat(&in_interleaved[0], in_interleaved.size(),
+ &in_interleaved[0]);
+ Deinterleave(&in_interleaved[0], in_buf.num_frames(),
+ in_buf.num_channels(), in_buf.channels());
+
if (FLAGS_perf) {
processing_start_time = TickTime::Now();
}
CHECK_EQ(kNoErr,
- ap->ProcessStream(c_buf.channels(),
- c_buf.num_frames(),
- c_file.sample_rate(),
- LayoutFromChannels(c_buf.num_channels()),
- o_file.sample_rate(),
- LayoutFromChannels(o_buf.num_channels()),
- o_buf.channels()));
+ ap->ProcessStream(in_buf.channels(),
+ in_buf.num_frames(),
+ in_file.sample_rate(),
+ LayoutFromChannels(in_buf.num_channels()),
+ out_file.sample_rate(),
+ LayoutFromChannels(out_buf.num_channels()),
+ out_buf.channels()));
if (FLAGS_perf) {
accumulated_time += TickTime::Now() - processing_start_time;
}
- Interleave(o_buf.channels(), o_buf.num_frames(),
- o_buf.num_channels(), o_interleaved.get());
- FloatToFloatS16(o_interleaved.get(), o_length, o_interleaved.get());
- o_file.WriteSamples(o_interleaved.get(), o_length);
+
+ Interleave(out_buf.channels(), out_buf.num_frames(),
+ out_buf.num_channels(), &out_interleaved[0]);
+ FloatToFloatS16(&out_interleaved[0], out_interleaved.size(),
+ &out_interleaved[0]);
+ out_file.WriteSamples(&out_interleaved[0], out_interleaved.size());
num_chunks++;
}
if (FLAGS_perf) {
diff --git a/webrtc/modules/audio_processing/test/process_test.cc b/webrtc/modules/audio_processing/test/process_test.cc
index 3218243..23d4d18 100644
--- a/webrtc/modules/audio_processing/test/process_test.cc
+++ b/webrtc/modules/audio_processing/test/process_test.cc
@@ -20,6 +20,7 @@
#include "webrtc/base/scoped_ptr.h"
#include "webrtc/common.h"
#include "webrtc/modules/audio_processing/include/audio_processing.h"
+#include "webrtc/modules/audio_processing/test/protobuf_utils.h"
#include "webrtc/modules/audio_processing/test/test_utils.h"
#include "webrtc/modules/interface/module_common_types.h"
#include "webrtc/system_wrappers/interface/cpu_features_wrapper.h"
@@ -905,7 +906,7 @@
// not reaching end-of-file.
EXPECT_EQ(0, fseek(near_file, read_count * sizeof(int16_t),
SEEK_CUR));
- break; // This is expected.
+ break; // This is expected.
}
} else {
ASSERT_EQ(size, read_count);
@@ -948,7 +949,7 @@
}
if (simulating) {
if (read_count != size) {
- break; // This is expected.
+ break; // This is expected.
}
delay_ms = 0;
@@ -1040,8 +1041,7 @@
size,
output_wav_file.get(),
output_raw_file.get());
- }
- else {
+ } else {
FAIL() << "Event " << event << " is unrecognized";
}
}
@@ -1136,8 +1136,7 @@
} // namespace
} // namespace webrtc
-int main(int argc, char* argv[])
-{
+int main(int argc, char* argv[]) {
webrtc::void_main(argc, argv);
// Optional, but removes memory leak noise from Valgrind.
diff --git a/webrtc/modules/audio_processing/test/protobuf_utils.cc b/webrtc/modules/audio_processing/test/protobuf_utils.cc
new file mode 100644
index 0000000..37042cd
--- /dev/null
+++ b/webrtc/modules/audio_processing/test/protobuf_utils.cc
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/modules/audio_processing/test/protobuf_utils.h"
+
+namespace webrtc {
+
+size_t ReadMessageBytesFromFile(FILE* file, rtc::scoped_ptr<uint8_t[]>* bytes) {
+ // The "wire format" for the size is little-endian. Assume we're running on
+ // a little-endian machine.
+#ifndef WEBRTC_ARCH_LITTLE_ENDIAN
+#error "Need to convert messsage from little-endian."
+#endif
+ int32_t size = 0;
+ if (fread(&size, sizeof(size), 1, file) != 1)
+ return 0;
+ if (size <= 0)
+ return 0;
+
+ bytes->reset(new uint8_t[size]);
+ return fread(bytes->get(), sizeof((*bytes)[0]), size, file);
+}
+
+// Returns true on success, false on error or end-of-file.
+bool ReadMessageFromFile(FILE* file, ::google::protobuf::MessageLite* msg) {
+ rtc::scoped_ptr<uint8_t[]> bytes;
+ size_t size = ReadMessageBytesFromFile(file, &bytes);
+ if (!size)
+ return false;
+
+ msg->Clear();
+ return msg->ParseFromArray(bytes.get(), size);
+}
+
+} // namespace webrtc
diff --git a/webrtc/modules/audio_processing/test/protobuf_utils.h b/webrtc/modules/audio_processing/test/protobuf_utils.h
new file mode 100644
index 0000000..230fcaa
--- /dev/null
+++ b/webrtc/modules/audio_processing/test/protobuf_utils.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_TEST_PROTOBUF_UTILS_H_
+#define WEBRTC_MODULES_AUDIO_PROCESSING_TEST_PROTOBUF_UTILS_H_
+
+#include "webrtc/audio_processing/debug.pb.h"
+#include "webrtc/base/scoped_ptr.h"
+
+namespace webrtc {
+
+// Allocates new memory in the scoped_ptr to fit the raw message and returns the
+// number of bytes read.
+size_t ReadMessageBytesFromFile(FILE* file, rtc::scoped_ptr<uint8_t[]>* bytes);
+
+// Returns true on success, false on error or end-of-file.
+bool ReadMessageFromFile(FILE* file, ::google::protobuf::MessageLite* msg);
+
+} // namespace webrtc
+
+#endif // WEBRTC_MODULES_AUDIO_PROCESSING_TEST_PROTOBUF_UTILS_H_
diff --git a/webrtc/modules/audio_processing/test/test_utils.cc b/webrtc/modules/audio_processing/test/test_utils.cc
new file mode 100644
index 0000000..fe33ec0
--- /dev/null
+++ b/webrtc/modules/audio_processing/test/test_utils.cc
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/checks.h"
+#include "webrtc/modules/audio_processing/test/test_utils.h"
+
+namespace webrtc {
+
+RawFile::RawFile(const std::string& filename)
+ : file_handle_(fopen(filename.c_str(), "wb")) {}
+
+RawFile::~RawFile() {
+ fclose(file_handle_);
+}
+
+void RawFile::WriteSamples(const int16_t* samples, size_t num_samples) {
+#ifndef WEBRTC_ARCH_LITTLE_ENDIAN
+#error "Need to convert samples to little-endian when writing to PCM file"
+#endif
+ fwrite(samples, sizeof(*samples), num_samples, file_handle_);
+}
+
+void RawFile::WriteSamples(const float* samples, size_t num_samples) {
+ fwrite(samples, sizeof(*samples), num_samples, file_handle_);
+}
+
+void WriteIntData(const int16_t* data,
+ size_t length,
+ WavWriter* wav_file,
+ RawFile* raw_file) {
+ if (wav_file) {
+ wav_file->WriteSamples(data, length);
+ }
+ if (raw_file) {
+ raw_file->WriteSamples(data, length);
+ }
+}
+
+void WriteFloatData(const float* const* data,
+ int samples_per_channel,
+ int num_channels,
+ WavWriter* wav_file,
+ RawFile* raw_file) {
+ size_t length = num_channels * samples_per_channel;
+ rtc::scoped_ptr<float[]> buffer(new float[length]);
+ Interleave(data, samples_per_channel, num_channels, buffer.get());
+ if (raw_file) {
+ raw_file->WriteSamples(buffer.get(), length);
+ }
+ // TODO(aluebs): Use ScaleToInt16Range() from audio_util
+ for (size_t i = 0; i < length; ++i) {
+ buffer[i] = buffer[i] > 0 ?
+ buffer[i] * std::numeric_limits<int16_t>::max() :
+ -buffer[i] * std::numeric_limits<int16_t>::min();
+ }
+ if (wav_file) {
+ wav_file->WriteSamples(buffer.get(), length);
+ }
+}
+
+FILE* OpenFile(const std::string& filename, const char* mode) {
+ FILE* file = fopen(filename.c_str(), mode);
+ if (!file) {
+ printf("Unable to open file %s\n", filename.c_str());
+ exit(1);
+ }
+ return file;
+}
+
+int SamplesFromRate(int rate) {
+ return AudioProcessing::kChunkSizeMs * rate / 1000;
+}
+
+void SetFrameSampleRate(AudioFrame* frame,
+ int sample_rate_hz) {
+ frame->sample_rate_hz_ = sample_rate_hz;
+ frame->samples_per_channel_ = AudioProcessing::kChunkSizeMs *
+ sample_rate_hz / 1000;
+}
+
+AudioProcessing::ChannelLayout LayoutFromChannels(int num_channels) {
+ switch (num_channels) {
+ case 1:
+ return AudioProcessing::kMono;
+ case 2:
+ return AudioProcessing::kStereo;
+ default:
+ assert(false);
+ return AudioProcessing::kMono;
+ }
+}
+
+std::vector<Point> ParseArrayGeometry(const std::string& mic_positions,
+ size_t num_mics) {
+ const std::vector<float> values = ParseList<float>(mic_positions);
+ CHECK_EQ(values.size(), 3 * num_mics) <<
+ "Could not parse mic_positions or incorrect number of points.";
+
+ std::vector<Point> result;
+ result.reserve(num_mics);
+ for (size_t i = 0; i < values.size(); i += 3) {
+ double x = values[i + 0];
+ double y = values[i + 1];
+ double z = values[i + 2];
+ result.push_back(Point(x, y, z));
+ }
+
+ return result;
+}
+
+
+} // namespace webrtc
diff --git a/webrtc/modules/audio_processing/test/test_utils.h b/webrtc/modules/audio_processing/test/test_utils.h
index 52274d7..7ad462c 100644
--- a/webrtc/modules/audio_processing/test/test_utils.h
+++ b/webrtc/modules/audio_processing/test/test_utils.h
@@ -8,13 +8,18 @@
* be found in the AUTHORS file in the root of the source tree.
*/
-#include <math.h>
-#include <limits>
+#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_TEST_TEST_UTILS_H_
+#define WEBRTC_MODULES_AUDIO_PROCESSING_TEST_TEST_UTILS_H_
-#include "webrtc/audio_processing/debug.pb.h"
+#include <math.h>
+#include <iterator>
+#include <limits>
+#include <string>
+#include <vector>
+
+#include "webrtc/base/constructormagic.h"
#include "webrtc/base/scoped_ptr.h"
#include "webrtc/common_audio/channel_buffer.h"
-#include "webrtc/common_audio/include/audio_util.h"
#include "webrtc/common_audio/wav_file.h"
#include "webrtc/modules/audio_processing/include/audio_processing.h"
#include "webrtc/modules/interface/module_common_types.h"
@@ -24,84 +29,38 @@
static const AudioProcessing::Error kNoErr = AudioProcessing::kNoError;
#define EXPECT_NOERR(expr) EXPECT_EQ(kNoErr, (expr))
-class RawFile {
+class RawFile final {
public:
- RawFile(const std::string& filename)
- : file_handle_(fopen(filename.c_str(), "wb")) {}
+ explicit RawFile(const std::string& filename);
+ ~RawFile();
- ~RawFile() {
- fclose(file_handle_);
- }
-
- void WriteSamples(const int16_t* samples, size_t num_samples) {
-#ifndef WEBRTC_ARCH_LITTLE_ENDIAN
-#error "Need to convert samples to little-endian when writing to PCM file"
-#endif
- fwrite(samples, sizeof(*samples), num_samples, file_handle_);
- }
-
- void WriteSamples(const float* samples, size_t num_samples) {
- fwrite(samples, sizeof(*samples), num_samples, file_handle_);
- }
+ void WriteSamples(const int16_t* samples, size_t num_samples);
+ void WriteSamples(const float* samples, size_t num_samples);
private:
FILE* file_handle_;
+
+ DISALLOW_COPY_AND_ASSIGN(RawFile);
};
-static inline void WriteIntData(const int16_t* data,
- size_t length,
- WavWriter* wav_file,
- RawFile* raw_file) {
- if (wav_file) {
- wav_file->WriteSamples(data, length);
- }
- if (raw_file) {
- raw_file->WriteSamples(data, length);
- }
-}
+void WriteIntData(const int16_t* data,
+ size_t length,
+ WavWriter* wav_file,
+ RawFile* raw_file);
-static inline void WriteFloatData(const float* const* data,
- size_t samples_per_channel,
- int num_channels,
- WavWriter* wav_file,
- RawFile* raw_file) {
- size_t length = num_channels * samples_per_channel;
- rtc::scoped_ptr<float[]> buffer(new float[length]);
- Interleave(data, samples_per_channel, num_channels, buffer.get());
- if (raw_file) {
- raw_file->WriteSamples(buffer.get(), length);
- }
- // TODO(aluebs): Use ScaleToInt16Range() from audio_util
- for (size_t i = 0; i < length; ++i) {
- buffer[i] = buffer[i] > 0 ?
- buffer[i] * std::numeric_limits<int16_t>::max() :
- -buffer[i] * std::numeric_limits<int16_t>::min();
- }
- if (wav_file) {
- wav_file->WriteSamples(buffer.get(), length);
- }
-}
+void WriteFloatData(const float* const* data,
+ int samples_per_channel,
+ int num_channels,
+ WavWriter* wav_file,
+ RawFile* raw_file);
// Exits on failure; do not use in unit tests.
-static inline FILE* OpenFile(const std::string& filename, const char* mode) {
- FILE* file = fopen(filename.c_str(), mode);
- if (!file) {
- printf("Unable to open file %s\n", filename.c_str());
- exit(1);
- }
- return file;
-}
+FILE* OpenFile(const std::string& filename, const char* mode);
-static inline int SamplesFromRate(int rate) {
- return AudioProcessing::kChunkSizeMs * rate / 1000;
-}
+int SamplesFromRate(int rate);
-static inline void SetFrameSampleRate(AudioFrame* frame,
- int sample_rate_hz) {
- frame->sample_rate_hz_ = sample_rate_hz;
- frame->samples_per_channel_ = AudioProcessing::kChunkSizeMs *
- sample_rate_hz / 1000;
-}
+void SetFrameSampleRate(AudioFrame* frame,
+ int sample_rate_hz);
template <typename T>
void SetContainerFormat(int sample_rate_hz,
@@ -113,47 +72,7 @@
cb->reset(new ChannelBuffer<T>(frame->samples_per_channel_, num_channels));
}
-static inline AudioProcessing::ChannelLayout LayoutFromChannels(
- int num_channels) {
- switch (num_channels) {
- case 1:
- return AudioProcessing::kMono;
- case 2:
- return AudioProcessing::kStereo;
- default:
- assert(false);
- return AudioProcessing::kMono;
- }
-}
-
-// Allocates new memory in the scoped_ptr to fit the raw message and returns the
-// number of bytes read.
-static inline size_t ReadMessageBytesFromFile(
- FILE* file,
- rtc::scoped_ptr<uint8_t[]>* bytes) {
- // The "wire format" for the size is little-endian. Assume we're running on
- // a little-endian machine.
- int32_t size = 0;
- if (fread(&size, sizeof(size), 1, file) != 1)
- return 0;
- if (size <= 0)
- return 0;
-
- bytes->reset(new uint8_t[size]);
- return fread(bytes->get(), sizeof((*bytes)[0]), size, file);
-}
-
-// Returns true on success, false on error or end-of-file.
-static inline bool ReadMessageFromFile(FILE* file,
- ::google::protobuf::MessageLite* msg) {
- rtc::scoped_ptr<uint8_t[]> bytes;
- size_t size = ReadMessageBytesFromFile(file, &bytes);
- if (!size)
- return false;
-
- msg->Clear();
- return msg->ParseFromArray(bytes.get(), size);
-}
+AudioProcessing::ChannelLayout LayoutFromChannels(int num_channels);
template <typename T>
float ComputeSNR(const T* ref, const T* test, int length, float* variance) {
@@ -177,4 +96,28 @@
return snr;
}
+// Returns a vector<T> parsed from whitespace delimited values in to_parse,
+// or an empty vector if the string could not be parsed.
+template<typename T>
+std::vector<T> ParseList(const std::string& to_parse) {
+ std::vector<T> values;
+
+ std::istringstream str(to_parse);
+ std::copy(
+ std::istream_iterator<T>(str),
+ std::istream_iterator<T>(),
+ std::back_inserter(values));
+
+ return values;
+}
+
+// Parses the array geometry from the command line.
+//
+// If a vector with size != num_mics is returned, an error has occurred and an
+// appropriate error message has been printed to stdout.
+std::vector<Point> ParseArrayGeometry(const std::string& mic_positions,
+ size_t num_mics);
+
} // namespace webrtc
+
+#endif // WEBRTC_MODULES_AUDIO_PROCESSING_TEST_TEST_UTILS_H_
diff --git a/webrtc/modules/audio_processing/test/unpack.cc b/webrtc/modules/audio_processing/test/unpack.cc
index 49c8b3c..2484828 100644
--- a/webrtc/modules/audio_processing/test/unpack.cc
+++ b/webrtc/modules/audio_processing/test/unpack.cc
@@ -18,6 +18,7 @@
#include "gflags/gflags.h"
#include "webrtc/audio_processing/debug.pb.h"
#include "webrtc/base/scoped_ptr.h"
+#include "webrtc/modules/audio_processing/test/protobuf_utils.h"
#include "webrtc/modules/audio_processing/test/test_utils.h"
#include "webrtc/typedefs.h"
diff --git a/webrtc/modules/modules.gyp b/webrtc/modules/modules.gyp
index 8392381..3a7e219 100644
--- a/webrtc/modules/modules.gyp
+++ b/webrtc/modules/modules.gyp
@@ -59,6 +59,8 @@
'audio_coding_module',
'audio_device' ,
'audio_processing',
+ 'audioproc_test_utils',
+ 'audioproc_protobuf_utils',
'bitrate_controller',
'bwe_simulator',
'CNG',
@@ -173,8 +175,6 @@
'audio_processing/beamformer/matrix_unittest.cc',
'audio_processing/beamformer/mock_nonlinear_beamformer.cc',
'audio_processing/beamformer/mock_nonlinear_beamformer.h',
- 'audio_processing/beamformer/pcm_utils.cc',
- 'audio_processing/beamformer/pcm_utils.h',
'audio_processing/echo_cancellation_impl_unittest.cc',
'audio_processing/splitting_filter_unittest.cc',
'audio_processing/transient/dyadic_decimator_unittest.cc',