summaryrefslogtreecommitdiff
path: root/common_audio
diff options
context:
space:
mode:
authorAndroid Chromium Automerger <chromium-automerger@android>2014-08-21 15:51:42 +0000
committerAndroid Chromium Automerger <chromium-automerger@android>2014-08-21 15:51:42 +0000
commite0ff458bb4794d90c8fa9dd338d7e9517e139f77 (patch)
tree53a313a83221a24a3149bb6b3a8146236bc0dbb4 /common_audio
parent11a9b6df83dd129ef51af4e1b4f1058eed29d3e2 (diff)
parent67afd1fc176021f625e064f20ae747e23d87d727 (diff)
downloadwebrtc-e0ff458bb4794d90c8fa9dd338d7e9517e139f77.tar.gz
Merge third_party/webrtc from https://chromium.googlesource.com/external/webrtc/trunk/webrtc.git at 67afd1fc176021f625e064f20ae747e23d87d727
This commit was generated by merge_from_chromium.py. Change-Id: I6c68c68562042b290acef201bb3d998bee1dc9be
Diffstat (limited to 'common_audio')
-rw-r--r--common_audio/common_audio.gyp6
-rw-r--r--common_audio/signal_processing/include/signal_processing_library.h18
-rw-r--r--common_audio/signal_processing/include/spl_inl_armv7.h6
-rw-r--r--common_audio/signal_processing/include/spl_inl_mips.h12
-rw-r--r--common_audio/signal_processing/signal_processing_unittest.cc6
-rw-r--r--common_audio/wav_header.cc152
-rw-r--r--common_audio/wav_header.h46
-rw-r--r--common_audio/wav_header_unittest.cc77
-rw-r--r--common_audio/wav_writer.cc103
-rw-r--r--common_audio/wav_writer.h65
-rw-r--r--common_audio/wav_writer_unittest.cc124
11 files changed, 573 insertions, 42 deletions
diff --git a/common_audio/common_audio.gyp b/common_audio/common_audio.gyp
index 3bed4e4d..58b9bb98 100644
--- a/common_audio/common_audio.gyp
+++ b/common_audio/common_audio.gyp
@@ -92,6 +92,10 @@
'vad/vad_gmm.h',
'vad/vad_sp.c',
'vad/vad_sp.h',
+ 'wav_header.cc',
+ 'wav_header.h',
+ 'wav_writer.cc',
+ 'wav_writer.h',
],
'conditions': [
['target_arch=="ia32" or target_arch=="x64"', {
@@ -211,6 +215,8 @@
'vad/vad_sp_unittest.cc',
'vad/vad_unittest.cc',
'vad/vad_unittest.h',
+ 'wav_header_unittest.cc',
+ 'wav_writer_unittest.cc',
],
'conditions': [
# TODO(henrike): remove build_with_chromium==1 when the bots are
diff --git a/common_audio/signal_processing/include/signal_processing_library.h b/common_audio/signal_processing/include/signal_processing_library.h
index 72a5388f..33a41ba2 100644
--- a/common_audio/signal_processing/include/signal_processing_library.h
+++ b/common_audio/signal_processing/include/signal_processing_library.h
@@ -36,19 +36,6 @@
#define WEBRTC_SPL_ABS_W32(a) \
(((int32_t)a >= 0) ? ((int32_t)a) : -((int32_t)a))
-#ifdef WEBRTC_ARCH_LITTLE_ENDIAN
-#define WEBRTC_SPL_GET_BYTE(a, nr) (((int8_t *)a)[nr])
-#define WEBRTC_SPL_SET_BYTE(d_ptr, val, index) \
- (((int8_t *)d_ptr)[index] = (val))
-#else
-#define WEBRTC_SPL_GET_BYTE(a, nr) \
- ((((int16_t *)a)[nr >> 1]) >> (((nr + 1) & 0x1) * 8) & 0x00ff)
-#define WEBRTC_SPL_SET_BYTE(d_ptr, val, index) \
- ((int16_t *)d_ptr)[index >> 1] = \
- ((((int16_t *)d_ptr)[index >> 1]) \
- & (0x00ff << (8 * ((index) & 0x1)))) | (val << (8 * ((index + 1) & 0x1)))
-#endif
-
#define WEBRTC_SPL_MUL(a, b) \
((int32_t) ((int32_t)(a) * (int32_t)(b)))
#define WEBRTC_SPL_UMUL(a, b) \
@@ -57,8 +44,6 @@
((uint32_t) (uint16_t)(a) * (uint16_t)(b))
#define WEBRTC_SPL_UMUL_32_16(a, b) \
((uint32_t) ((uint32_t)(a) * (uint16_t)(b)))
-#define WEBRTC_SPL_UMUL_32_16_RSFT16(a, b) \
- ((uint32_t) ((uint32_t)(a) * (uint16_t)(b)) >> 16)
#define WEBRTC_SPL_MUL_16_U16(a, b) \
((int32_t)(int16_t)(a) * (uint16_t)(b))
#define WEBRTC_SPL_DIV(a, b) \
@@ -132,9 +117,6 @@ extern "C" {
#define WEBRTC_SPL_MEMCPY_W16(v1, v2, length) \
memcpy(v1, v2, (length) * sizeof(int16_t))
-#define WEBRTC_SPL_MEMMOVE_W16(v1, v2, length) \
- memmove(v1, v2, (length) * sizeof(int16_t))
-
// inline functions:
#include "webrtc/common_audio/signal_processing/include/spl_inl.h"
diff --git a/common_audio/signal_processing/include/spl_inl_armv7.h b/common_audio/signal_processing/include/spl_inl_armv7.h
index b52ebc53..0f505471 100644
--- a/common_audio/signal_processing/include/spl_inl_armv7.h
+++ b/common_audio/signal_processing/include/spl_inl_armv7.h
@@ -30,12 +30,6 @@ static __inline int32_t WEBRTC_SPL_MUL_16_32_RSFT16(int16_t a, int32_t b) {
return tmp;
}
-static __inline int32_t WEBRTC_SPL_MUL_32_32_RSFT32BI(int32_t a, int32_t b) {
- int32_t tmp = 0;
- __asm volatile ("smmulr %0, %1, %2":"=r"(tmp):"r"(a), "r"(b));
- return tmp;
-}
-
static __inline int32_t WEBRTC_SPL_MUL_16_16(int16_t a, int16_t b) {
int32_t tmp = 0;
__asm __volatile ("smulbb %0, %1, %2":"=r"(tmp):"r"(a), "r"(b));
diff --git a/common_audio/signal_processing/include/spl_inl_mips.h b/common_audio/signal_processing/include/spl_inl_mips.h
index f29f701d..ab9a60d4 100644
--- a/common_audio/signal_processing/include/spl_inl_mips.h
+++ b/common_audio/signal_processing/include/spl_inl_mips.h
@@ -66,18 +66,6 @@ static __inline int32_t WEBRTC_SPL_MUL_16_32_RSFT16(int16_t a,
return value32;
}
-static __inline int32_t WEBRTC_SPL_MUL_32_32_RSFT32BI(int32_t a,
- int32_t b) {
- int32_t tmp = 0;
-
- if ((32767 < a) || (a < 0))
- tmp = WEBRTC_SPL_MUL_16_32_RSFT16(((int16_t)(a >> 16)), b);
- tmp += WEBRTC_SPL_MUL_16_32_RSFT16(((int16_t)((a & 0x0000FFFF) >> 1)),
- b) >> 15;
-
- return tmp;
-}
-
#if defined(MIPS_DSP_R1_LE)
static __inline int16_t WebRtcSpl_SatW32ToW16(int32_t value32) {
__asm __volatile(
diff --git a/common_audio/signal_processing/signal_processing_unittest.cc b/common_audio/signal_processing/signal_processing_unittest.cc
index 603294be..146afae5 100644
--- a/common_audio/signal_processing/signal_processing_unittest.cc
+++ b/common_audio/signal_processing/signal_processing_unittest.cc
@@ -30,17 +30,12 @@ TEST_F(SplTest, MacroTest) {
int B = 21;
int a = -3;
int b = WEBRTC_SPL_WORD32_MAX;
- int nr = 2;
- int d_ptr2 = 0;
EXPECT_EQ(10, WEBRTC_SPL_MIN(A, B));
EXPECT_EQ(21, WEBRTC_SPL_MAX(A, B));
EXPECT_EQ(3, WEBRTC_SPL_ABS_W16(a));
EXPECT_EQ(3, WEBRTC_SPL_ABS_W32(a));
- EXPECT_EQ(0, WEBRTC_SPL_GET_BYTE(&B, nr));
- WEBRTC_SPL_SET_BYTE(&d_ptr2, 1, nr);
- EXPECT_EQ(65536, d_ptr2);
EXPECT_EQ(-63, WEBRTC_SPL_MUL(a, B));
EXPECT_EQ(-2147483645, WEBRTC_SPL_MUL(a, b));
@@ -48,7 +43,6 @@ TEST_F(SplTest, MacroTest) {
b = WEBRTC_SPL_WORD16_MAX >> 1;
EXPECT_EQ(1073627139u, WEBRTC_SPL_UMUL_16_16(a, b));
EXPECT_EQ(4294918147u, WEBRTC_SPL_UMUL_32_16(a, b));
- EXPECT_EQ(65535u, WEBRTC_SPL_UMUL_32_16_RSFT16(a, b));
EXPECT_EQ(-49149, WEBRTC_SPL_MUL_16_U16(a, b));
a = b;
diff --git a/common_audio/wav_header.cc b/common_audio/wav_header.cc
new file mode 100644
index 00000000..ce43896f
--- /dev/null
+++ b/common_audio/wav_header.cc
@@ -0,0 +1,152 @@
+/*
+ * 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.
+ */
+
+// Based on the WAV file format documentation at
+// https://ccrma.stanford.edu/courses/422/projects/WaveFormat/ and
+// http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/WAVE.html
+
+#include "webrtc/common_audio/wav_header.h"
+
+#include <algorithm>
+#include <cstring>
+#include <limits>
+
+#include "webrtc/common_audio/include/audio_util.h"
+
+namespace webrtc {
+
+struct ChunkHeader {
+ uint32_t ID;
+ uint32_t Size;
+};
+COMPILE_ASSERT(sizeof(ChunkHeader) == 8, chunk_header_size);
+
+bool CheckWavParameters(int num_channels,
+ int sample_rate,
+ WavFormat format,
+ int bytes_per_sample,
+ uint32_t num_samples) {
+ // num_channels, sample_rate, and bytes_per_sample must be positive, must fit
+ // in their respective fields, and their product must fit in the 32-bit
+ // ByteRate field.
+ if (num_channels <= 0 || sample_rate <= 0 || bytes_per_sample <= 0)
+ return false;
+ if (static_cast<uint64_t>(sample_rate) > std::numeric_limits<uint32_t>::max())
+ return false;
+ if (static_cast<uint64_t>(num_channels) >
+ std::numeric_limits<uint16_t>::max())
+ return false;
+ if (static_cast<uint64_t>(bytes_per_sample) * 8 >
+ std::numeric_limits<uint16_t>::max())
+ return false;
+ if (static_cast<uint64_t>(sample_rate) * num_channels * bytes_per_sample >
+ std::numeric_limits<uint32_t>::max())
+ return false;
+
+ // format and bytes_per_sample must agree.
+ switch (format) {
+ case kWavFormatPcm:
+ // Other values may be OK, but for now we're conservative:
+ if (bytes_per_sample != 1 && bytes_per_sample != 2)
+ return false;
+ break;
+ case kWavFormatALaw:
+ case kWavFormatMuLaw:
+ if (bytes_per_sample != 1)
+ return false;
+ break;
+ default:
+ return false;
+ }
+
+ // The number of bytes in the file, not counting the first ChunkHeader, must
+ // be less than 2^32; otherwise, the ChunkSize field overflows.
+ const uint32_t max_samples =
+ (std::numeric_limits<uint32_t>::max()
+ - (kWavHeaderSize - sizeof(ChunkHeader))) /
+ bytes_per_sample;
+ if (num_samples > max_samples)
+ return false;
+
+ // Each channel must have the same number of samples.
+ if (num_samples % num_channels != 0)
+ return false;
+
+ return true;
+}
+
+#ifdef WEBRTC_ARCH_LITTLE_ENDIAN
+static inline void WriteLE16(uint16_t* f, uint16_t x) { *f = x; }
+static inline void WriteLE32(uint32_t* f, uint32_t x) { *f = x; }
+static inline void WriteFourCC(uint32_t* f, char a, char b, char c, char d) {
+ *f = static_cast<uint32_t>(a)
+ | static_cast<uint32_t>(b) << 8
+ | static_cast<uint32_t>(c) << 16
+ | static_cast<uint32_t>(d) << 24;
+}
+#else
+#error "Write be-to-le conversion functions"
+#endif
+
+void 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));
+
+ 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);
+
+ 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));
+ 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));
+ 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);
+ WriteLE16(&header.fmt.BitsPerSample, 8 * bytes_per_sample);
+
+ WriteFourCC(&header.data.header.ID, 'd', 'a', 't', 'a');
+ WriteLE32(&header.data.header.Size, bytes_in_payload);
+
+ // Do an extra copy rather than writing everything to buf directly, since buf
+ // might not be correctly aligned.
+ memcpy(buf, &header, kWavHeaderSize);
+}
+
+} // namespace webrtc
diff --git a/common_audio/wav_header.h b/common_audio/wav_header.h
new file mode 100644
index 00000000..f9ed8a57
--- /dev/null
+++ b/common_audio/wav_header.h
@@ -0,0 +1,46 @@
+/*
+ * 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_HEADER_H_
+#define WEBRTC_COMMON_AUDIO_WAV_HEADER_H_
+
+#include <stdint.h>
+
+namespace webrtc {
+
+static const int kWavHeaderSize = 44;
+
+enum WavFormat {
+ kWavFormatPcm = 1, // PCM, each sample of size bytes_per_sample
+ kWavFormatALaw = 6, // 8-bit ITU-T G.711 A-law
+ kWavFormatMuLaw = 7, // 8-bit ITU-T G.711 mu-law
+};
+
+// Return true if the given parameters will make a well-formed WAV header.
+bool CheckWavParameters(int num_channels,
+ int sample_rate,
+ WavFormat format,
+ int bytes_per_sample,
+ uint32_t num_samples);
+
+// 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,
+ 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
new file mode 100644
index 00000000..f05160ea
--- /dev/null
+++ b/common_audio/wav_header_unittest.cc
@@ -0,0 +1,77 @@
+/*
+ * 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 <limits>
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webrtc/common_audio/wav_header.h"
+#include "webrtc/system_wrappers/interface/compile_assert.h"
+
+// Try various choices of WAV header parameters, and make sure that the good
+// ones are accepted and the bad ones rejected.
+TEST(WavHeaderTest, CheckWavParameters) {
+ // Try some really stupid values for one parameter at a time.
+ EXPECT_TRUE(webrtc::CheckWavParameters(1, 8000, webrtc::kWavFormatPcm, 1, 0));
+ EXPECT_FALSE(
+ webrtc::CheckWavParameters(0, 8000, webrtc::kWavFormatPcm, 1, 0));
+ EXPECT_FALSE(
+ webrtc::CheckWavParameters(-1, 8000, webrtc::kWavFormatPcm, 1, 0));
+ EXPECT_FALSE(webrtc::CheckWavParameters(1, 0, webrtc::kWavFormatPcm, 1, 0));
+ EXPECT_FALSE(webrtc::CheckWavParameters(1, 8000, webrtc::WavFormat(0), 1, 0));
+ EXPECT_FALSE(
+ webrtc::CheckWavParameters(1, 8000, webrtc::kWavFormatPcm, 0, 0));
+
+ // Try invalid format/bytes-per-sample combinations.
+ EXPECT_TRUE(webrtc::CheckWavParameters(1, 8000, webrtc::kWavFormatPcm, 2, 0));
+ EXPECT_FALSE(
+ webrtc::CheckWavParameters(1, 8000, webrtc::kWavFormatPcm, 4, 0));
+ EXPECT_FALSE(
+ webrtc::CheckWavParameters(1, 8000, webrtc::kWavFormatALaw, 2, 0));
+ EXPECT_FALSE(
+ webrtc::CheckWavParameters(1, 8000, webrtc::kWavFormatMuLaw, 2, 0));
+
+ // Too large values.
+ EXPECT_FALSE(webrtc::CheckWavParameters(
+ 1 << 20, 1 << 20, webrtc::kWavFormatPcm, 1, 0));
+ EXPECT_FALSE(webrtc::CheckWavParameters(
+ 1, 8000, webrtc::kWavFormatPcm, 1, std::numeric_limits<uint32_t>::max()));
+
+ // Not the same number of samples for each channel.
+ EXPECT_FALSE(
+ webrtc::CheckWavParameters(3, 8000, webrtc::kWavFormatPcm, 1, 5));
+}
+
+// Try writing a WAV header and make sure it looks OK.
+TEST(WavHeaderTest, WriteWavHeader) {
+ 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);
+ static const uint8_t kExpectedBuf[] = {
+ 0xa4, 0xa4, 0xa4, 0xa4, // untouched bytes before header
+ '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
+ 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
+ };
+ COMPILE_ASSERT(sizeof(kExpectedBuf) == kSize, buf_size);
+ EXPECT_EQ(0, memcmp(kExpectedBuf, buf, kSize));
+}
diff --git a/common_audio/wav_writer.cc b/common_audio/wav_writer.cc
new file mode 100644
index 00000000..e5714735
--- /dev/null
+++ b/common_audio/wav_writer.cc
@@ -0,0 +1,103 @@
+/*
+ * 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")) {
+ FATAL_ERROR_IF(!CheckWavParameters(num_channels_,
+ sample_rate_,
+ kWavFormat,
+ kBytesPerSample,
+ num_samples_));
+ FATAL_ERROR_IF(!file_handle_);
+
+ // 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};
+ FATAL_ERROR_IF(fwrite(blank_header, kWavHeaderSize, 1, file_handle_) != 1);
+}
+
+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_);
+ FATAL_ERROR_IF(written != num_samples);
+ num_samples_ += static_cast<uint32_t>(written);
+ FATAL_ERROR_IF(written > std::numeric_limits<uint32_t>::max() ||
+ num_samples_ < written); // detect uint32_t overflow
+ FATAL_ERROR_IF(!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);
+ RoundToInt16(samples + i, chunk, isamples);
+ WriteSamples(isamples, chunk);
+ }
+}
+
+void WavFile::Close() {
+ FATAL_ERROR_IF(fseek(file_handle_, 0, SEEK_SET) != 0);
+ uint8_t header[kWavHeaderSize];
+ WriteWavHeader(header, num_channels_, sample_rate_, kWavFormat,
+ kBytesPerSample, num_samples_);
+ FATAL_ERROR_IF(fwrite(header, kWavHeaderSize, 1, file_handle_) != 1);
+ FATAL_ERROR_IF(fclose(file_handle_) != 0);
+ 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);
+}
diff --git a/common_audio/wav_writer.h b/common_audio/wav_writer.h
new file mode 100644
index 00000000..a8fee51e
--- /dev/null
+++ b/common_audio/wav_writer.h
@@ -0,0 +1,65 @@
+/*
+ * 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 FATAL_ERROR(), 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);
+
+ private:
+ void WriteSamples(const int16_t* samples, size_t num_samples);
+ 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);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // WEBRTC_COMMON_AUDIO_WAV_WRITER_H_
diff --git a/common_audio/wav_writer_unittest.cc b/common_audio/wav_writer_unittest.cc
new file mode 100644
index 00000000..9efe96e3
--- /dev/null
+++ b/common_audio/wav_writer_unittest.cc
@@ -0,0 +1,124 @@
+/*
+ * 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.
+ */
+
+// MSVC++ requires this to be set before any other includes to get M_PI.
+#define _USE_MATH_DEFINES
+
+#include <cmath>
+#include <limits>
+
+#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/test/testsupport/fileutils.h"
+
+static const float kSamples[] = {0.0, 10.0, 4e4, -1e9};
+
+// Write a tiny WAV file with the C++ interface and verify the result.
+TEST(WavWriterTest, CPP) {
+ const std::string outfile = webrtc::test::OutputPath() + "wavtest1.wav";
+ static const int kNumSamples = 3;
+ {
+ webrtc::WavFile w(outfile, 14099, 1);
+ w.WriteSamples(kSamples, kNumSamples);
+ }
+ static const uint8_t kExpectedContents[] = {
+ 'R', 'I', 'F', 'F',
+ 42, 0, 0, 0, // size of whole file - 8: 6 + 44 - 8
+ 'W', 'A', 'V', 'E',
+ 'f', 'm', 't', ' ',
+ 16, 0, 0, 0, // size of fmt block - 8: 24 - 8
+ 1, 0, // format: PCM (1)
+ 1, 0, // channels: 1
+ 0x13, 0x37, 0, 0, // sample rate: 14099
+ 0x26, 0x6e, 0, 0, // byte rate: 2 * 14099
+ 2, 0, // block align: NumChannels * BytesPerSample
+ 16, 0, // bits per sample: 2 * 8
+ 'd', 'a', 't', 'a',
+ 6, 0, 0, 0, // size of payload: 6
+ 0, 0, // first sample: 0.0
+ 10, 0, // second sample: 10.0
+ 0xff, 0x7f, // third sample: 4e4 (saturated)
+ };
+ static const int kContentSize =
+ webrtc::kWavHeaderSize + kNumSamples * sizeof(int16_t);
+ COMPILE_ASSERT(sizeof(kExpectedContents) == kContentSize, content_size);
+ EXPECT_EQ(size_t(kContentSize), webrtc::test::GetFileSize(outfile));
+ FILE* f = fopen(outfile.c_str(), "rb");
+ ASSERT_TRUE(f);
+ uint8_t contents[kContentSize];
+ ASSERT_EQ(1u, fread(contents, kContentSize, 1, f));
+ EXPECT_EQ(0, fclose(f));
+ EXPECT_EQ(0, memcmp(kExpectedContents, contents, kContentSize));
+}
+
+// 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);
+ static const int kNumSamples = 4;
+ rtc_WavWriteSamples(w, &kSamples[0], 2);
+ rtc_WavWriteSamples(w, &kSamples[2], kNumSamples - 2);
+ rtc_WavClose(w);
+ static const uint8_t kExpectedContents[] = {
+ 'R', 'I', 'F', 'F',
+ 44, 0, 0, 0, // size of whole file - 8: 8 + 44 - 8
+ 'W', 'A', 'V', 'E',
+ 'f', 'm', 't', ' ',
+ 16, 0, 0, 0, // size of fmt block - 8: 24 - 8
+ 1, 0, // format: PCM (1)
+ 2, 0, // channels: 2
+ 0x80, 0x2e, 0, 0, // sample rate: 11904
+ 0, 0xba, 0, 0, // byte rate: 2 * 2 * 11904
+ 4, 0, // block align: NumChannels * BytesPerSample
+ 16, 0, // bits per sample: 2 * 8
+ 'd', 'a', 't', 'a',
+ 8, 0, 0, 0, // size of payload: 8
+ 0, 0, // first sample: 0.0
+ 10, 0, // second sample: 10.0
+ 0xff, 0x7f, // third sample: 4e4 (saturated)
+ 0, 0x80, // fourth sample: -1e9 (saturated)
+ };
+ static const int kContentSize =
+ webrtc::kWavHeaderSize + kNumSamples * sizeof(int16_t);
+ COMPILE_ASSERT(sizeof(kExpectedContents) == kContentSize, content_size);
+ EXPECT_EQ(size_t(kContentSize), webrtc::test::GetFileSize(outfile));
+ FILE* f = fopen(outfile.c_str(), "rb");
+ ASSERT_TRUE(f);
+ uint8_t contents[kContentSize];
+ ASSERT_EQ(1u, fread(contents, kContentSize, 1, f));
+ EXPECT_EQ(0, fclose(f));
+ EXPECT_EQ(0, memcmp(kExpectedContents, contents, kContentSize));
+}
+
+// Write a larger WAV file. You can listen to this file to sanity-check it.
+TEST(WavWriterTest, LargeFile) {
+ std::string outfile = webrtc::test::OutputPath() + "wavtest3.wav";
+ static const int kSampleRate = 8000;
+ static const int kNumChannels = 2;
+ static const int kNumSamples = 3 * kSampleRate * kNumChannels;
+ float samples[kNumSamples];
+ for (int i = 0; i < kNumSamples; i += kNumChannels) {
+ // A nice periodic beeping sound.
+ static const double kToneHz = 440;
+ const double t = static_cast<double>(i) / (kNumChannels * kSampleRate);
+ const double x =
+ std::numeric_limits<int16_t>::max() * std::sin(t * kToneHz * 2 * M_PI);
+ samples[i] = std::pow(std::sin(t * 2 * 2 * M_PI), 10) * x;
+ samples[i + 1] = std::pow(std::cos(t * 2 * 2 * M_PI), 10) * x;
+ }
+ {
+ webrtc::WavFile w(outfile, kSampleRate, kNumChannels);
+ w.WriteSamples(samples, kNumSamples);
+ }
+ EXPECT_EQ(sizeof(int16_t) * kNumSamples + webrtc::kWavHeaderSize,
+ webrtc::test::GetFileSize(outfile));
+}