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',