Add a WavReader counterpart to WavWriter.

Don't bother with a C interface as we currently have no need to call
this from C code. The first use will be in the audioproc tool.

R=kwiberg@webrtc.org

Review URL: https://webrtc-codereview.appspot.com/30829004

git-svn-id: http://webrtc.googlecode.com/svn/trunk/webrtc@7585 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/common_audio/BUILD.gn b/common_audio/BUILD.gn
index 1338a75..ba1d179 100644
--- a/common_audio/BUILD.gn
+++ b/common_audio/BUILD.gn
@@ -85,8 +85,8 @@
     "vad/vad_sp.h",
     "wav_header.cc",
     "wav_header.h",
-    "wav_writer.cc",
-    "wav_writer.h",
+    "wav_file.cc",
+    "wav_file.h",
     "window_generator.cc",
     "window_generator.h",
   ]
diff --git a/common_audio/common_audio.gyp b/common_audio/common_audio.gyp
index ca77ba8..8f96674 100644
--- a/common_audio/common_audio.gyp
+++ b/common_audio/common_audio.gyp
@@ -99,8 +99,8 @@
         'vad/vad_sp.h',
         'wav_header.cc',
         'wav_header.h',
-        'wav_writer.cc',
-        'wav_writer.h',
+        'wav_file.cc',
+        'wav_file.h',
         'window_generator.cc',
         'window_generator.h',
       ],
@@ -245,7 +245,7 @@
             'vad/vad_unittest.cc',
             'vad/vad_unittest.h',
             'wav_header_unittest.cc',
-            'wav_writer_unittest.cc',
+            'wav_file_unittest.cc',
             'window_generator_unittest.cc',
           ],
           'conditions': [
diff --git a/common_audio/wav_file.cc b/common_audio/wav_file.cc
new file mode 100644
index 0000000..685f353
--- /dev/null
+++ b/common_audio/wav_file.cc
@@ -0,0 +1,166 @@
+/*
+ *  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/common_audio/wav_file.h"
+
+#include <algorithm>
+#include <cstdio>
+#include <limits>
+
+#include "webrtc/base/checks.h"
+#include "webrtc/common_audio/include/audio_util.h"
+#include "webrtc/common_audio/wav_header.h"
+
+namespace webrtc {
+
+// We write 16-bit PCM WAV files.
+static const WavFormat kWavFormat = kWavFormatPcm;
+static const int kBytesPerSample = 2;
+
+WavReader::WavReader(const std::string& filename)
+    : file_handle_(fopen(filename.c_str(), "rb")) {
+  CHECK(file_handle_);
+  uint8_t header[kWavHeaderSize];
+  const size_t read =
+      fread(header, sizeof(*header), kWavHeaderSize, file_handle_);
+  CHECK_EQ(kWavHeaderSize, read);
+
+  WavFormat format;
+  int bytes_per_sample;
+  CHECK(ReadWavHeader(header, &num_channels_, &sample_rate_, &format,
+                      &bytes_per_sample, &num_samples_));
+  CHECK_EQ(kWavFormat, format);
+  CHECK_EQ(kBytesPerSample, bytes_per_sample);
+}
+
+WavReader::~WavReader() {
+  Close();
+}
+
+size_t WavReader::ReadSamples(size_t num_samples, int16_t* samples) {
+#ifndef WEBRTC_ARCH_LITTLE_ENDIAN
+#error "Need to convert samples to big-endian when reading from WAV file"
+#endif
+  const size_t read =
+      fread(samples, sizeof(*samples), num_samples, file_handle_);
+  // If we didn't read what was requested, ensure we've reached the EOF.
+  CHECK(read == num_samples || feof(file_handle_));
+  return read;
+}
+
+size_t WavReader::ReadSamples(size_t num_samples, float* samples) {
+  static const size_t kChunksize = 4096 / sizeof(uint16_t);
+  size_t read = 0;
+  for (size_t i = 0; i < num_samples; i += kChunksize) {
+    int16_t isamples[kChunksize];
+    size_t chunk = std::min(kChunksize, num_samples - i);
+    chunk = ReadSamples(chunk, isamples);
+    for (size_t j = 0; j < chunk; ++j)
+      samples[i + j] = isamples[j];
+    read += chunk;
+  }
+  return read;
+}
+
+void WavReader::Close() {
+  CHECK_EQ(0, fclose(file_handle_));
+  file_handle_ = NULL;
+}
+
+WavWriter::WavWriter(const std::string& filename, int sample_rate,
+                     int num_channels)
+    : sample_rate_(sample_rate),
+      num_channels_(num_channels),
+      num_samples_(0),
+      file_handle_(fopen(filename.c_str(), "wb")) {
+  CHECK(file_handle_);
+  CHECK(CheckWavParameters(num_channels_,
+                           sample_rate_,
+                           kWavFormat,
+                           kBytesPerSample,
+                           num_samples_));
+
+  // Write a blank placeholder header, since we need to know the total number
+  // of samples before we can fill in the real data.
+  static const uint8_t blank_header[kWavHeaderSize] = {0};
+  CHECK_EQ(1u, fwrite(blank_header, kWavHeaderSize, 1, file_handle_));
+}
+
+WavWriter::~WavWriter() {
+  Close();
+}
+
+void WavWriter::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 WAV file"
+#endif
+  const size_t written =
+      fwrite(samples, sizeof(*samples), num_samples, file_handle_);
+  CHECK_EQ(num_samples, written);
+  num_samples_ += static_cast<uint32_t>(written);
+  CHECK(written <= std::numeric_limits<uint32_t>::max() ||
+        num_samples_ >= written);  // detect uint32_t overflow
+  CHECK(CheckWavParameters(num_channels_,
+                           sample_rate_,
+                           kWavFormat,
+                           kBytesPerSample,
+                           num_samples_));
+}
+
+void WavWriter::WriteSamples(const float* samples, size_t num_samples) {
+  static const size_t kChunksize = 4096 / sizeof(uint16_t);
+  for (size_t i = 0; i < num_samples; i += kChunksize) {
+    int16_t isamples[kChunksize];
+    const size_t chunk = std::min(kChunksize, num_samples - i);
+    FloatS16ToS16(samples + i, chunk, isamples);
+    WriteSamples(isamples, chunk);
+  }
+}
+
+void WavWriter::Close() {
+  CHECK_EQ(0, fseek(file_handle_, 0, SEEK_SET));
+  uint8_t header[kWavHeaderSize];
+  CHECK(WriteWavHeader(header, num_channels_, sample_rate_, kWavFormat,
+                       kBytesPerSample, num_samples_));
+  CHECK_EQ(1u, fwrite(header, kWavHeaderSize, 1, file_handle_));
+  CHECK_EQ(0, fclose(file_handle_));
+  file_handle_ = NULL;
+}
+
+}  // namespace webrtc
+
+rtc_WavWriter* rtc_WavOpen(const char* filename,
+                           int sample_rate,
+                           int num_channels) {
+  return reinterpret_cast<rtc_WavWriter*>(
+      new webrtc::WavWriter(filename, sample_rate, num_channels));
+}
+
+void rtc_WavClose(rtc_WavWriter* wf) {
+  delete reinterpret_cast<webrtc::WavWriter*>(wf);
+}
+
+void rtc_WavWriteSamples(rtc_WavWriter* wf,
+                         const float* samples,
+                         size_t num_samples) {
+  reinterpret_cast<webrtc::WavWriter*>(wf)->WriteSamples(samples, num_samples);
+}
+
+int rtc_WavSampleRate(const rtc_WavWriter* wf) {
+  return reinterpret_cast<const webrtc::WavWriter*>(wf)->sample_rate();
+}
+
+int rtc_WavNumChannels(const rtc_WavWriter* wf) {
+  return reinterpret_cast<const webrtc::WavWriter*>(wf)->num_channels();
+}
+
+uint32_t rtc_WavNumSamples(const rtc_WavWriter* wf) {
+  return reinterpret_cast<const webrtc::WavWriter*>(wf)->num_samples();
+}
diff --git a/common_audio/wav_file.h b/common_audio/wav_file.h
new file mode 100644
index 0000000..c6c5d6b
--- /dev/null
+++ b/common_audio/wav_file.h
@@ -0,0 +1,98 @@
+/*
+ *  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_COMMON_AUDIO_WAV_FILE_H_
+#define WEBRTC_COMMON_AUDIO_WAV_FILE_H_
+
+#ifdef __cplusplus
+
+#include <stdint.h>
+#include <cstddef>
+#include <string>
+
+namespace webrtc {
+
+// Simple C++ class for writing 16-bit PCM WAV files. All error handling is
+// by calls to CHECK(), making it unsuitable for anything but debug code.
+class WavWriter {
+ public:
+  // Open a new WAV file for writing.
+  WavWriter(const std::string& filename, int sample_rate, int num_channels);
+
+  // Close the WAV file, after writing its header.
+  ~WavWriter();
+
+  // Write additional samples to the file. Each sample is in the range
+  // [-32768,32767], and there must be the previously specified number of
+  // interleaved channels.
+  void WriteSamples(const float* samples, size_t num_samples);
+  void WriteSamples(const int16_t* samples, size_t num_samples);
+
+  int sample_rate() const { return sample_rate_; }
+  int num_channels() const { return num_channels_; }
+  uint32_t num_samples() const { return num_samples_; }
+
+ private:
+  void Close();
+  const int sample_rate_;
+  const int num_channels_;
+  uint32_t num_samples_;  // Total number of samples written to file.
+  FILE* file_handle_;  // Output file, owned by this class
+};
+
+// Follows the conventions of WavWriter.
+class WavReader {
+ public:
+  // Opens an existing WAV file for reading.
+  explicit WavReader(const std::string& filename);
+
+  // Close the WAV file.
+  ~WavReader();
+
+  // Returns the number of samples read. If this is less than requested,
+  // verifies that the end of the file was reached.
+  size_t ReadSamples(size_t num_samples, float* samples);
+  size_t ReadSamples(size_t num_samples, int16_t* samples);
+
+  int sample_rate() const { return sample_rate_; }
+  int num_channels() const { return num_channels_; }
+  uint32_t num_samples() const { return num_samples_; }
+
+ private:
+  void Close();
+  int sample_rate_;
+  int num_channels_;
+  uint32_t num_samples_;  // Total number of samples in the file.
+  FILE* file_handle_;  // Input file, owned by this class.
+};
+
+}  // namespace webrtc
+
+extern "C" {
+#endif  // __cplusplus
+
+// C wrappers for the WavWriter class.
+typedef struct rtc_WavWriter rtc_WavWriter;
+rtc_WavWriter* rtc_WavOpen(const char* filename,
+                           int sample_rate,
+                           int num_channels);
+void rtc_WavClose(rtc_WavWriter* wf);
+void rtc_WavWriteSamples(rtc_WavWriter* wf,
+                         const float* samples,
+                         size_t num_samples);
+int rtc_WavSampleRate(const rtc_WavWriter* wf);
+int rtc_WavNumChannels(const rtc_WavWriter* wf);
+uint32_t rtc_WavNumSamples(const rtc_WavWriter* wf);
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // WEBRTC_COMMON_AUDIO_WAV_FILE_H_
diff --git a/common_audio/wav_writer_unittest.cc b/common_audio/wav_file_unittest.cc
similarity index 82%
rename from common_audio/wav_writer_unittest.cc
rename to common_audio/wav_file_unittest.cc
index 9c593be..1bdb655 100644
--- a/common_audio/wav_writer_unittest.cc
+++ b/common_audio/wav_file_unittest.cc
@@ -17,7 +17,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "webrtc/base/compile_assert.h"
 #include "webrtc/common_audio/wav_header.h"
-#include "webrtc/common_audio/wav_writer.h"
+#include "webrtc/common_audio/wav_file.h"
 #include "webrtc/test/testsupport/fileutils.h"
 
 static const float kSamples[] = {0.0, 10.0, 4e4, -1e9};
@@ -27,7 +27,7 @@
   const std::string outfile = webrtc::test::OutputPath() + "wavtest1.wav";
   static const uint32_t kNumSamples = 3;
   {
-    webrtc::WavFile w(outfile, 14099, 1);
+    webrtc::WavWriter w(outfile, 14099, 1);
     EXPECT_EQ(14099, w.sample_rate());
     EXPECT_EQ(1, w.num_channels());
     EXPECT_EQ(0u, w.num_samples());
@@ -62,12 +62,24 @@
   ASSERT_EQ(1u, fread(contents, kContentSize, 1, f));
   EXPECT_EQ(0, fclose(f));
   EXPECT_EQ(0, memcmp(kExpectedContents, contents, kContentSize));
+
+  {
+    webrtc::WavReader r(outfile);
+    EXPECT_EQ(14099, r.sample_rate());
+    EXPECT_EQ(1, r.num_channels());
+    EXPECT_EQ(kNumSamples, r.num_samples());
+    static const float kTruncatedSamples[] = {0.0, 10.0, 32767.0};
+    float samples[kNumSamples];
+    EXPECT_EQ(kNumSamples, r.ReadSamples(kNumSamples, samples));
+    EXPECT_EQ(0, memcmp(kTruncatedSamples, samples, sizeof(samples)));
+    EXPECT_EQ(0u, r.ReadSamples(kNumSamples, samples));
+  }
 }
 
 // Write a tiny WAV file with the C interface and verify the result.
 TEST(WavWriterTest, C) {
   const std::string outfile = webrtc::test::OutputPath() + "wavtest2.wav";
-  rtc_WavFile *w = rtc_WavOpen(outfile.c_str(), 11904, 2);
+  rtc_WavWriter *w = rtc_WavOpen(outfile.c_str(), 11904, 2);
   EXPECT_EQ(11904, rtc_WavSampleRate(w));
   EXPECT_EQ(2, rtc_WavNumChannels(w));
   EXPECT_EQ(0u, rtc_WavNumSamples(w));
@@ -125,7 +137,7 @@
     samples[i + 1] = std::pow(std::cos(t * 2 * 2 * M_PI), 10) * x;
   }
   {
-    webrtc::WavFile w(outfile, kSampleRate, kNumChannels);
+    webrtc::WavWriter w(outfile, kSampleRate, kNumChannels);
     EXPECT_EQ(kSampleRate, w.sample_rate());
     EXPECT_EQ(kNumChannels, w.num_channels());
     EXPECT_EQ(0u, w.num_samples());
@@ -134,4 +146,18 @@
   }
   EXPECT_EQ(sizeof(int16_t) * kNumSamples + webrtc::kWavHeaderSize,
             webrtc::test::GetFileSize(outfile));
+
+  {
+    webrtc::WavReader r(outfile);
+    EXPECT_EQ(kSampleRate, r.sample_rate());
+    EXPECT_EQ(kNumChannels, r.num_channels());
+    EXPECT_EQ(kNumSamples, r.num_samples());
+
+    float read_samples[kNumSamples];
+    EXPECT_EQ(kNumSamples, r.ReadSamples(kNumSamples, read_samples));
+    for (size_t i = 0; i < kNumSamples; ++i)
+      EXPECT_NEAR(samples[i], read_samples[i], 1);
+
+    EXPECT_EQ(0u, r.ReadSamples(kNumSamples, read_samples));
+  }
 }
diff --git a/common_audio/wav_header.cc b/common_audio/wav_header.cc
index ce43896..3182b1f 100644
--- a/common_audio/wav_header.cc
+++ b/common_audio/wav_header.cc
@@ -18,9 +18,11 @@
 #include <cstring>
 #include <limits>
 
+#include "webrtc/base/checks.h"
 #include "webrtc/common_audio/include/audio_util.h"
 
 namespace webrtc {
+namespace {
 
 struct ChunkHeader {
   uint32_t ID;
@@ -28,6 +30,34 @@
 };
 COMPILE_ASSERT(sizeof(ChunkHeader) == 8, chunk_header_size);
 
+// We can't nest this definition in WavHeader, because VS2013 gives an error
+// on sizeof(WavHeader::fmt): "error C2070: 'unknown': illegal sizeof operand".
+struct FmtSubchunk {
+  ChunkHeader header;
+  uint16_t AudioFormat;
+  uint16_t NumChannels;
+  uint32_t SampleRate;
+  uint32_t ByteRate;
+  uint16_t BlockAlign;
+  uint16_t BitsPerSample;
+};
+COMPILE_ASSERT(sizeof(FmtSubchunk) == 24, fmt_subchunk_size);
+const uint32_t kFmtSubchunkSize = sizeof(FmtSubchunk) - sizeof(ChunkHeader);
+
+struct WavHeader {
+  struct {
+    ChunkHeader header;
+    uint32_t Format;
+  } riff;
+  FmtSubchunk fmt;
+  struct {
+    ChunkHeader header;
+  } data;
+};
+COMPILE_ASSERT(sizeof(WavHeader) == kWavHeaderSize, no_padding_in_header);
+
+}  // namespace
+
 bool CheckWavParameters(int num_channels,
                         int sample_rate,
                         WavFormat format,
@@ -91,54 +121,54 @@
       | static_cast<uint32_t>(c) << 16
       | static_cast<uint32_t>(d) << 24;
 }
+
+static inline uint16_t ReadLE16(uint16_t x) { return x; }
+static inline uint32_t ReadLE32(uint32_t x) { return x; }
+static inline std::string ReadFourCC(uint32_t x) {
+  return std::string(reinterpret_cast<char*>(&x), 4);
+}
 #else
 #error "Write be-to-le conversion functions"
 #endif
 
-void WriteWavHeader(uint8_t* buf,
+static inline uint32_t RiffChunkSize(uint32_t bytes_in_payload) {
+  return bytes_in_payload + kWavHeaderSize - sizeof(ChunkHeader);
+}
+
+static inline uint32_t ByteRate(int num_channels, int sample_rate,
+                                int bytes_per_sample) {
+  return static_cast<uint32_t>(num_channels) * sample_rate * bytes_per_sample;
+}
+
+static inline uint16_t BlockAlign(int num_channels, int bytes_per_sample) {
+  return num_channels * bytes_per_sample;
+}
+
+bool WriteWavHeader(uint8_t* buf,
                     int num_channels,
                     int sample_rate,
                     WavFormat format,
                     int bytes_per_sample,
                     uint32_t num_samples) {
-  assert(CheckWavParameters(num_channels, sample_rate, format,
-                            bytes_per_sample, num_samples));
+  if (!CheckWavParameters(num_channels, sample_rate, format,
+                          bytes_per_sample, num_samples))
+    return false;
 
-  struct {
-    struct {
-      ChunkHeader header;
-      uint32_t Format;
-    } riff;
-    struct {
-      ChunkHeader header;
-      uint16_t AudioFormat;
-      uint16_t NumChannels;
-      uint32_t SampleRate;
-      uint32_t ByteRate;
-      uint16_t BlockAlign;
-      uint16_t BitsPerSample;
-    } fmt;
-    struct {
-      ChunkHeader header;
-    } data;
-  } header;
-  COMPILE_ASSERT(sizeof(header) == kWavHeaderSize, no_padding_in_header);
-
+  WavHeader header;
   const uint32_t bytes_in_payload = bytes_per_sample * num_samples;
 
   WriteFourCC(&header.riff.header.ID, 'R', 'I', 'F', 'F');
-  WriteLE32(&header.riff.header.Size,
-            bytes_in_payload + kWavHeaderSize - sizeof(ChunkHeader));
+  WriteLE32(&header.riff.header.Size, RiffChunkSize(bytes_in_payload));
   WriteFourCC(&header.riff.Format, 'W', 'A', 'V', 'E');
 
   WriteFourCC(&header.fmt.header.ID, 'f', 'm', 't', ' ');
-  WriteLE32(&header.fmt.header.Size, sizeof(header.fmt) - sizeof(ChunkHeader));
+  WriteLE32(&header.fmt.header.Size, kFmtSubchunkSize);
   WriteLE16(&header.fmt.AudioFormat, format);
   WriteLE16(&header.fmt.NumChannels, num_channels);
   WriteLE32(&header.fmt.SampleRate, sample_rate);
-  WriteLE32(&header.fmt.ByteRate, (static_cast<uint32_t>(num_channels)
-                                   * sample_rate * bytes_per_sample));
-  WriteLE16(&header.fmt.BlockAlign, num_channels * bytes_per_sample);
+  WriteLE32(&header.fmt.ByteRate, ByteRate(num_channels, sample_rate,
+                                           bytes_per_sample));
+  WriteLE16(&header.fmt.BlockAlign, BlockAlign(num_channels, bytes_per_sample));
   WriteLE16(&header.fmt.BitsPerSample, 8 * bytes_per_sample);
 
   WriteFourCC(&header.data.header.ID, 'd', 'a', 't', 'a');
@@ -147,6 +177,52 @@
   // Do an extra copy rather than writing everything to buf directly, since buf
   // might not be correctly aligned.
   memcpy(buf, &header, kWavHeaderSize);
+  return true;
 }
 
+bool ReadWavHeader(const uint8_t* buf,
+                   int* num_channels,
+                   int* sample_rate,
+                   WavFormat* format,
+                   int* bytes_per_sample,
+                   uint32_t* num_samples) {
+  WavHeader header;
+  memcpy(&header, buf, kWavHeaderSize);
+
+  // Parse needed fields.
+  *format = static_cast<WavFormat>(ReadLE16(header.fmt.AudioFormat));
+  *num_channels = ReadLE16(header.fmt.NumChannels);
+  *sample_rate = ReadLE32(header.fmt.SampleRate);
+  *bytes_per_sample = ReadLE16(header.fmt.BitsPerSample) / 8;
+  const uint32_t bytes_in_payload = ReadLE32(header.data.header.Size);
+  if (*bytes_per_sample <= 0)
+    return false;
+  *num_samples = bytes_in_payload / *bytes_per_sample;
+
+  // Sanity check remaining fields.
+  if (ReadFourCC(header.riff.header.ID) != "RIFF")
+    return false;
+  if (ReadFourCC(header.riff.Format) != "WAVE")
+    return false;
+  if (ReadFourCC(header.fmt.header.ID) != "fmt ")
+    return false;
+  if (ReadFourCC(header.data.header.ID) != "data")
+    return false;
+
+  if (ReadLE32(header.riff.header.Size) != RiffChunkSize(bytes_in_payload))
+    return false;
+  if (ReadLE32(header.fmt.header.Size) != kFmtSubchunkSize)
+    return false;
+  if (ReadLE32(header.fmt.ByteRate) !=
+      ByteRate(*num_channels, *sample_rate, *bytes_per_sample))
+    return false;
+  if (ReadLE16(header.fmt.BlockAlign) !=
+      BlockAlign(*num_channels, *bytes_per_sample))
+    return false;
+
+  return CheckWavParameters(*num_channels, *sample_rate, *format,
+                            *bytes_per_sample, *num_samples);
+}
+
+
 }  // namespace webrtc
diff --git a/common_audio/wav_header.h b/common_audio/wav_header.h
index f9ed8a5..0901f97 100644
--- a/common_audio/wav_header.h
+++ b/common_audio/wav_header.h
@@ -11,11 +11,12 @@
 #ifndef WEBRTC_COMMON_AUDIO_WAV_HEADER_H_
 #define WEBRTC_COMMON_AUDIO_WAV_HEADER_H_
 
+#include <stddef.h>
 #include <stdint.h>
 
 namespace webrtc {
 
-static const int kWavHeaderSize = 44;
+static const size_t kWavHeaderSize = 44;
 
 enum WavFormat {
   kWavFormatPcm   = 1,  // PCM, each sample of size bytes_per_sample
@@ -33,14 +34,23 @@
 // Write a kWavHeaderSize bytes long WAV header to buf. The payload that
 // follows the header is supposed to have the specified number of interleaved
 // channels and contain the specified total number of samples of the specified
-// type.
-void WriteWavHeader(uint8_t* buf,
+// type. Returns false if any of the input parameters are invalid.
+bool WriteWavHeader(uint8_t* buf,
                     int num_channels,
                     int sample_rate,
                     WavFormat format,
                     int bytes_per_sample,
                     uint32_t num_samples);
 
+// Read a kWavHeaderSize bytes long WAV header from buf and parse the values
+// into the provided output parameters. Returns false if the header is invalid.
+bool ReadWavHeader(const uint8_t* buf,
+                   int* num_channels,
+                   int* sample_rate,
+                   WavFormat* format,
+                   int* bytes_per_sample,
+                   uint32_t* num_samples);
+
 }  // namespace webrtc
 
 #endif  // WEBRTC_COMMON_AUDIO_WAV_HEADER_H_
diff --git a/common_audio/wav_header_unittest.cc b/common_audio/wav_header_unittest.cc
index f05160e..ac79cc1 100644
--- a/common_audio/wav_header_unittest.cc
+++ b/common_audio/wav_header_unittest.cc
@@ -48,13 +48,85 @@
       webrtc::CheckWavParameters(3, 8000, webrtc::kWavFormatPcm, 1, 5));
 }
 
+TEST(WavHeaderTest, ReadWavHeader) {
+  int num_channels = 0;
+  int sample_rate = 0;
+  webrtc::WavFormat format = webrtc::kWavFormatPcm;
+  int bytes_per_sample = 0;
+  uint32_t num_samples = 0;
+
+  // Test a few ways the header can be invalid. We start with the valid header
+  // used in WriteAndReadWavHeader, and invalidate one field per test. The
+  // invalid field is indicated in the array name, and in the comments with
+  // *BAD*.
+  static const uint8_t kBadRiffID[] = {
+    'R', 'i', 'f', 'f',  // *BAD*
+    0xbd, 0xd0, 0x5b, 0x07,  // size of whole file - 8: 123457689 + 44 - 8
+    'W', 'A', 'V', 'E',
+    'f', 'm', 't', ' ',
+    16, 0, 0, 0,  // size of fmt block - 8: 24 - 8
+    6, 0,  // format: A-law (6)
+    17, 0,  // channels: 17
+    0x39, 0x30, 0, 0,  // sample rate: 12345
+    0xc9, 0x33, 0x03, 0,  // byte rate: 1 * 17 * 12345
+    17, 0,  // block align: NumChannels * BytesPerSample
+    8, 0,  // bits per sample: 1 * 8
+    'd', 'a', 't', 'a',
+    0x99, 0xd0, 0x5b, 0x07,  // size of payload: 123457689
+    0xa4, 0xa4, 0xa4, 0xa4,  // untouched bytes after header
+  };
+  EXPECT_FALSE(
+      webrtc::ReadWavHeader(kBadRiffID, &num_channels, &sample_rate,
+                            &format, &bytes_per_sample, &num_samples));
+
+  static const uint8_t kBadBitsPerSample[] = {
+    'R', 'I', 'F', 'F',
+    0xbd, 0xd0, 0x5b, 0x07,  // size of whole file - 8: 123457689 + 44 - 8
+    'W', 'A', 'V', 'E',
+    'f', 'm', 't', ' ',
+    16, 0, 0, 0,  // size of fmt block - 8: 24 - 8
+    6, 0,  // format: A-law (6)
+    17, 0,  // channels: 17
+    0x39, 0x30, 0, 0,  // sample rate: 12345
+    0xc9, 0x33, 0x03, 0,  // byte rate: 1 * 17 * 12345
+    17, 0,  // block align: NumChannels * BytesPerSample
+    1, 0,  // bits per sample: *BAD*
+    'd', 'a', 't', 'a',
+    0x99, 0xd0, 0x5b, 0x07,  // size of payload: 123457689
+    0xa4, 0xa4, 0xa4, 0xa4,  // untouched bytes after header
+  };
+  EXPECT_FALSE(
+      webrtc::ReadWavHeader(kBadBitsPerSample, &num_channels, &sample_rate,
+                            &format, &bytes_per_sample, &num_samples));
+
+  static const uint8_t kBadByteRate[] = {
+    'R', 'I', 'F', 'F',
+    0xbd, 0xd0, 0x5b, 0x07,  // size of whole file - 8: 123457689 + 44 - 8
+    'W', 'A', 'V', 'E',
+    'f', 'm', 't', ' ',
+    16, 0, 0, 0,  // size of fmt block - 8: 24 - 8
+    6, 0,  // format: A-law (6)
+    17, 0,  // channels: 17
+    0x39, 0x30, 0, 0,  // sample rate: 12345
+    0x00, 0x33, 0x03, 0,  // byte rate: *BAD*
+    17, 0,  // block align: NumChannels * BytesPerSample
+    8, 0,  // bits per sample: 1 * 8
+    'd', 'a', 't', 'a',
+    0x99, 0xd0, 0x5b, 0x07,  // size of payload: 123457689
+    0xa4, 0xa4, 0xa4, 0xa4,  // untouched bytes after header
+  };
+  EXPECT_FALSE(
+      webrtc::ReadWavHeader(kBadByteRate, &num_channels, &sample_rate,
+                            &format, &bytes_per_sample, &num_samples));
+}
+
 // Try writing a WAV header and make sure it looks OK.
-TEST(WavHeaderTest, WriteWavHeader) {
+TEST(WavHeaderTest, WriteAndReadWavHeader) {
   static const int kSize = 4 + webrtc::kWavHeaderSize + 4;
   uint8_t buf[kSize];
   memset(buf, 0xa4, sizeof(buf));
-  webrtc::WriteWavHeader(
-      buf + 4, 17, 12345, webrtc::kWavFormatALaw, 1, 123457689);
+  EXPECT_TRUE(webrtc::WriteWavHeader(
+      buf + 4, 17, 12345, webrtc::kWavFormatALaw, 1, 123457689));
   static const uint8_t kExpectedBuf[] = {
     0xa4, 0xa4, 0xa4, 0xa4,  // untouched bytes before header
     'R', 'I', 'F', 'F',
@@ -74,4 +146,18 @@
   };
   COMPILE_ASSERT(sizeof(kExpectedBuf) == kSize, buf_size);
   EXPECT_EQ(0, memcmp(kExpectedBuf, buf, kSize));
+
+  int num_channels = 0;
+  int sample_rate = 0;
+  webrtc::WavFormat format = webrtc::kWavFormatPcm;
+  int bytes_per_sample = 0;
+  uint32_t num_samples = 0;
+  EXPECT_TRUE(
+      webrtc::ReadWavHeader(buf + 4, &num_channels, &sample_rate, &format,
+                            &bytes_per_sample, &num_samples));
+  EXPECT_EQ(17, num_channels);
+  EXPECT_EQ(12345, sample_rate);
+  EXPECT_EQ(webrtc::kWavFormatALaw, format);
+  EXPECT_EQ(1, bytes_per_sample);
+  EXPECT_EQ(123457689u, num_samples);
 }
diff --git a/common_audio/wav_writer.cc b/common_audio/wav_writer.cc
deleted file mode 100644
index 5244978..0000000
--- a/common_audio/wav_writer.cc
+++ /dev/null
@@ -1,115 +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/common_audio/wav_writer.h"
-
-#include <algorithm>
-#include <cstdio>
-#include <limits>
-
-#include "webrtc/base/checks.h"
-#include "webrtc/common_audio/include/audio_util.h"
-#include "webrtc/common_audio/wav_header.h"
-
-namespace webrtc {
-
-// We write 16-bit PCM WAV files.
-static const WavFormat kWavFormat = kWavFormatPcm;
-static const int kBytesPerSample = 2;
-
-WavFile::WavFile(const std::string& filename, int sample_rate, int num_channels)
-    : sample_rate_(sample_rate),
-      num_channels_(num_channels),
-      num_samples_(0),
-      file_handle_(fopen(filename.c_str(), "wb")) {
-  CHECK(file_handle_);
-  CHECK(CheckWavParameters(num_channels_,
-                           sample_rate_,
-                           kWavFormat,
-                           kBytesPerSample,
-                           num_samples_));
-
-  // Write a blank placeholder header, since we need to know the total number
-  // of samples before we can fill in the real data.
-  static const uint8_t blank_header[kWavHeaderSize] = {0};
-  CHECK_EQ(1u, fwrite(blank_header, kWavHeaderSize, 1, file_handle_));
-}
-
-WavFile::~WavFile() {
-  Close();
-}
-
-void WavFile::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 WAV file"
-#endif
-  const size_t written =
-      fwrite(samples, sizeof(*samples), num_samples, file_handle_);
-  CHECK_EQ(num_samples, written);
-  num_samples_ += static_cast<uint32_t>(written);
-  CHECK(written <= std::numeric_limits<uint32_t>::max() ||
-        num_samples_ >= written);  // detect uint32_t overflow
-  CHECK(CheckWavParameters(num_channels_,
-                           sample_rate_,
-                           kWavFormat,
-                           kBytesPerSample,
-                           num_samples_));
-}
-
-void WavFile::WriteSamples(const float* samples, size_t num_samples) {
-  static const size_t kChunksize = 4096 / sizeof(uint16_t);
-  for (size_t i = 0; i < num_samples; i += kChunksize) {
-    int16_t isamples[kChunksize];
-    const size_t chunk = std::min(kChunksize, num_samples - i);
-    FloatS16ToS16(samples + i, chunk, isamples);
-    WriteSamples(isamples, chunk);
-  }
-}
-
-void WavFile::Close() {
-  CHECK_EQ(0, fseek(file_handle_, 0, SEEK_SET));
-  uint8_t header[kWavHeaderSize];
-  WriteWavHeader(header, num_channels_, sample_rate_, kWavFormat,
-                 kBytesPerSample, num_samples_);
-  CHECK_EQ(1u, fwrite(header, kWavHeaderSize, 1, file_handle_));
-  CHECK_EQ(0, fclose(file_handle_));
-  file_handle_ = NULL;
-}
-
-}  // namespace webrtc
-
-rtc_WavFile* rtc_WavOpen(const char* filename,
-                         int sample_rate,
-                         int num_channels) {
-  return reinterpret_cast<rtc_WavFile*>(
-      new webrtc::WavFile(filename, sample_rate, num_channels));
-}
-
-void rtc_WavClose(rtc_WavFile* wf) {
-  delete reinterpret_cast<webrtc::WavFile*>(wf);
-}
-
-void rtc_WavWriteSamples(rtc_WavFile* wf,
-                         const float* samples,
-                         size_t num_samples) {
-  reinterpret_cast<webrtc::WavFile*>(wf)->WriteSamples(samples, num_samples);
-}
-
-int rtc_WavSampleRate(const rtc_WavFile* wf) {
-  return reinterpret_cast<const webrtc::WavFile*>(wf)->sample_rate();
-}
-
-int rtc_WavNumChannels(const rtc_WavFile* wf) {
-  return reinterpret_cast<const webrtc::WavFile*>(wf)->num_channels();
-}
-
-uint32_t rtc_WavNumSamples(const rtc_WavFile* wf) {
-  return reinterpret_cast<const webrtc::WavFile*>(wf)->num_samples();
-}
diff --git a/common_audio/wav_writer.h b/common_audio/wav_writer.h
deleted file mode 100644
index 0966727..0000000
--- a/common_audio/wav_writer.h
+++ /dev/null
@@ -1,72 +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_COMMON_AUDIO_WAV_WRITER_H_
-#define WEBRTC_COMMON_AUDIO_WAV_WRITER_H_
-
-#ifdef __cplusplus
-
-#include <stdint.h>
-#include <cstddef>
-#include <string>
-
-namespace webrtc {
-
-// Simple C++ class for writing 16-bit PCM WAV files. All error handling is
-// by calls to CHECK(), making it unsuitable for anything but debug code.
-class WavFile {
- public:
-  // Open a new WAV file for writing.
-  WavFile(const std::string& filename, int sample_rate, int num_channels);
-
-  // Close the WAV file, after writing its header.
-  ~WavFile();
-
-  // Write additional samples to the file. Each sample is in the range
-  // [-32768,32767], and there must be the previously specified number of
-  // interleaved channels.
-  void WriteSamples(const float* samples, size_t num_samples);
-  void WriteSamples(const int16_t* samples, size_t num_samples);
-
-  int sample_rate() const { return sample_rate_; }
-  int num_channels() const { return num_channels_; }
-  uint32_t num_samples() const { return num_samples_; }
-
- private:
-  void Close();
-  const int sample_rate_;
-  const int num_channels_;
-  uint32_t num_samples_;  // total number of samples written to file
-  FILE* file_handle_;  // output file, owned by this class
-};
-
-}  // namespace webrtc
-
-extern "C" {
-#endif  // __cplusplus
-
-// C wrappers for the WavFile class.
-typedef struct rtc_WavFile rtc_WavFile;
-rtc_WavFile* rtc_WavOpen(const char* filename,
-                         int sample_rate,
-                         int num_channels);
-void rtc_WavClose(rtc_WavFile* wf);
-void rtc_WavWriteSamples(rtc_WavFile* wf,
-                         const float* samples,
-                         size_t num_samples);
-int rtc_WavSampleRate(const rtc_WavFile* wf);
-int rtc_WavNumChannels(const rtc_WavFile* wf);
-uint32_t rtc_WavNumSamples(const rtc_WavFile* wf);
-
-#ifdef __cplusplus
-}  // extern "C"
-#endif
-
-#endif  // WEBRTC_COMMON_AUDIO_WAV_WRITER_H_
diff --git a/modules/audio_processing/aec/aec_core.c b/modules/audio_processing/aec/aec_core.c
index 1e217eb..50457d9 100644
--- a/modules/audio_processing/aec/aec_core.c
+++ b/modules/audio_processing/aec/aec_core.c
@@ -1351,7 +1351,7 @@
 #ifdef WEBRTC_AEC_DEBUG_DUMP
 // Open a new Wav file for writing. If it was already open with a different
 // sample frequency, close it first.
-static void ReopenWav(rtc_WavFile** wav_file,
+static void ReopenWav(rtc_WavWriter** wav_file,
                       const char* name,
                       int seq1,
                       int seq2,
diff --git a/modules/audio_processing/aec/aec_core_internal.h b/modules/audio_processing/aec/aec_core_internal.h
index 6adc4d6..5e30366 100644
--- a/modules/audio_processing/aec/aec_core_internal.h
+++ b/modules/audio_processing/aec/aec_core_internal.h
@@ -11,7 +11,7 @@
 #ifndef WEBRTC_MODULES_AUDIO_PROCESSING_AEC_AEC_CORE_INTERNAL_H_
 #define WEBRTC_MODULES_AUDIO_PROCESSING_AEC_AEC_CORE_INTERNAL_H_
 
-#include "webrtc/common_audio/wav_writer.h"
+#include "webrtc/common_audio/wav_file.h"
 #include "webrtc/modules/audio_processing/aec/aec_common.h"
 #include "webrtc/modules/audio_processing/aec/aec_core.h"
 #include "webrtc/modules/audio_processing/utility/ring_buffer.h"
@@ -147,10 +147,10 @@
   int debug_dump_count;
 
   RingBuffer* far_time_buf;
-  rtc_WavFile* farFile;
-  rtc_WavFile* nearFile;
-  rtc_WavFile* outFile;
-  rtc_WavFile* outLinearFile;
+  rtc_WavWriter* farFile;
+  rtc_WavWriter* nearFile;
+  rtc_WavWriter* outFile;
+  rtc_WavWriter* outLinearFile;
 #endif
 };
 
diff --git a/modules/audio_processing/test/process_test.cc b/modules/audio_processing/test/process_test.cc
index b6d51e4..76a916d 100644
--- a/modules/audio_processing/test/process_test.cc
+++ b/modules/audio_processing/test/process_test.cc
@@ -489,7 +489,7 @@
   FILE* aecm_echo_path_in_file = NULL;
   FILE* aecm_echo_path_out_file = NULL;
 
-  scoped_ptr<WavFile> output_wav_file;
+  scoped_ptr<WavWriter> output_wav_file;
   scoped_ptr<RawFile> output_raw_file;
 
   if (pb_filename) {
@@ -637,9 +637,9 @@
         if (!raw_output) {
           // The WAV file needs to be reset every time, because it cant change
           // it's sample rate or number of channels.
-          output_wav_file.reset(new WavFile(out_filename + ".wav",
-                                            output_sample_rate,
-                                            msg.num_output_channels()));
+          output_wav_file.reset(new WavWriter(out_filename + ".wav",
+                                              output_sample_rate,
+                                              msg.num_output_channels()));
         }
 
       } else if (event_msg.type() == Event::REVERSE_STREAM) {
@@ -876,9 +876,9 @@
         if (!raw_output) {
           // The WAV file needs to be reset every time, because it can't change
           // it's sample rate or number of channels.
-          output_wav_file.reset(new WavFile(out_filename + ".wav",
-                                            sample_rate_hz,
-                                            num_capture_output_channels));
+          output_wav_file.reset(new WavWriter(out_filename + ".wav",
+                                              sample_rate_hz,
+                                              num_capture_output_channels));
         }
 
         if (verbose) {
@@ -1029,9 +1029,9 @@
           output_raw_file.reset(new RawFile(out_filename + ".pcm"));
         }
         if (!raw_output && !output_wav_file) {
-          output_wav_file.reset(new WavFile(out_filename + ".wav",
-                                            sample_rate_hz,
-                                            num_capture_output_channels));
+          output_wav_file.reset(new WavWriter(out_filename + ".wav",
+                                              sample_rate_hz,
+                                              num_capture_output_channels));
         }
         WriteIntData(near_frame.data_,
                      size,
diff --git a/modules/audio_processing/test/test_utils.h b/modules/audio_processing/test/test_utils.h
index a99f342..d0d08cb 100644
--- a/modules/audio_processing/test/test_utils.h
+++ b/modules/audio_processing/test/test_utils.h
@@ -13,7 +13,7 @@
 
 #include "webrtc/audio_processing/debug.pb.h"
 #include "webrtc/common_audio/include/audio_util.h"
-#include "webrtc/common_audio/wav_writer.h"
+#include "webrtc/common_audio/wav_file.h"
 #include "webrtc/modules/audio_processing/common.h"
 #include "webrtc/modules/audio_processing/include/audio_processing.h"
 #include "webrtc/modules/interface/module_common_types.h"
@@ -50,7 +50,7 @@
 
 static inline void WriteIntData(const int16_t* data,
                                 size_t length,
-                                WavFile* wav_file,
+                                WavWriter* wav_file,
                                 RawFile* raw_file) {
   if (wav_file) {
     wav_file->WriteSamples(data, length);
@@ -63,7 +63,7 @@
 static inline void WriteFloatData(const float* const* data,
                                   size_t samples_per_channel,
                                   int num_channels,
-                                  WavFile* wav_file,
+                                  WavWriter* wav_file,
                                   RawFile* raw_file) {
   size_t length = num_channels * samples_per_channel;
   scoped_ptr<float[]> buffer(new float[length]);
diff --git a/modules/audio_processing/test/unpack.cc b/modules/audio_processing/test/unpack.cc
index 249b668..ee4e263 100644
--- a/modules/audio_processing/test/unpack.cc
+++ b/modules/audio_processing/test/unpack.cc
@@ -73,9 +73,9 @@
   int num_reverse_channels = 0;
   int num_input_channels = 0;
   int num_output_channels = 0;
-  scoped_ptr<WavFile> reverse_wav_file;
-  scoped_ptr<WavFile> input_wav_file;
-  scoped_ptr<WavFile> output_wav_file;
+  scoped_ptr<WavWriter> reverse_wav_file;
+  scoped_ptr<WavWriter> input_wav_file;
+  scoped_ptr<WavWriter> output_wav_file;
   scoped_ptr<RawFile> reverse_raw_file;
   scoped_ptr<RawFile> input_raw_file;
   scoped_ptr<RawFile> output_raw_file;
@@ -235,15 +235,15 @@
       if (!FLAGS_raw) {
         // The WAV files need to be reset every time, because they cant change
         // their sample rate or number of channels.
-        reverse_wav_file.reset(new WavFile(FLAGS_reverse_file + ".wav",
-                                           reverse_sample_rate,
-                                           num_reverse_channels));
-        input_wav_file.reset(new WavFile(FLAGS_input_file + ".wav",
-                                         input_sample_rate,
-                                         num_input_channels));
-        output_wav_file.reset(new WavFile(FLAGS_output_file + ".wav",
-                                          output_sample_rate,
-                                          num_output_channels));
+        reverse_wav_file.reset(new WavWriter(FLAGS_reverse_file + ".wav",
+                                             reverse_sample_rate,
+                                             num_reverse_channels));
+        input_wav_file.reset(new WavWriter(FLAGS_input_file + ".wav",
+                                           input_sample_rate,
+                                           num_input_channels));
+        output_wav_file.reset(new WavWriter(FLAGS_output_file + ".wav",
+                                            output_sample_rate,
+                                            num_output_channels));
       }
     }
   }