summaryrefslogtreecommitdiff
path: root/common_audio
diff options
context:
space:
mode:
authorkwiberg@webrtc.org <kwiberg@webrtc.org@4adac7df-926f-26a2-2b94-8c16560cd09d>2014-08-20 07:42:46 +0000
committerkwiberg@webrtc.org <kwiberg@webrtc.org@4adac7df-926f-26a2-2b94-8c16560cd09d>2014-08-20 07:42:46 +0000
commitddd80b5eacf50958c4335eb5a535e07aa8b05369 (patch)
treebe2ffcbff221826488472f8a807e8385566a67ee /common_audio
parent39377b82ddae9a3eb0b679a20100b9bdf98759e1 (diff)
downloadwebrtc-ddd80b5eacf50958c4335eb5a535e07aa8b05369.tar.gz
New utility class for easy debug dumping to WAV files
There are currently a number of places in the code where we dump audio data in various stages of processing for debug purposes. Currently these all write raw, uncompressed PCM files, which isn't supported by the most common audio players, and requires the user to supply metadata such as sample rate, sample size and endianness, etc. This patch adds a simple class that makes it easy to write WAV files instead. WAV files still contain the same uncompressed PCM data, but they have a small header that contains all the requisite metadata, and are supported by virtually all audio players. Since some of the debug code that will be writing WAV files is written in plain C, a C API is included as well. R=andrew@webrtc.org, bjornv@webrtc.org, henrike@webrtc.org, tommi@webrtc.org Review URL: https://webrtc-codereview.appspot.com/16809004 git-svn-id: http://webrtc.googlecode.com/svn/trunk/webrtc@6932 4adac7df-926f-26a2-2b94-8c16560cd09d
Diffstat (limited to 'common_audio')
-rw-r--r--common_audio/common_audio.gyp6
-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
7 files changed, 573 insertions, 0 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/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));
+}