summaryrefslogtreecommitdiff
path: root/common_audio
diff options
context:
space:
mode:
authorandrew@webrtc.org <andrew@webrtc.org>2014-10-31 21:51:03 +0000
committerandrew@webrtc.org <andrew@webrtc.org>2014-10-31 21:51:03 +0000
commit46c5634b0a617aa58e5ee12729709776cb73f26b (patch)
treeccbb26ca0460fe87258de6ddb2e14fd62eebc02f /common_audio
parent796056b8fff85d6e85e3717d3598684fc9e893f0 (diff)
downloadwebrtc-46c5634b0a617aa58e5ee12729709776cb73f26b.tar.gz
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
Diffstat (limited to 'common_audio')
-rw-r--r--common_audio/BUILD.gn4
-rw-r--r--common_audio/common_audio.gyp6
-rw-r--r--common_audio/wav_file.cc166
-rw-r--r--common_audio/wav_file.h98
-rw-r--r--common_audio/wav_file_unittest.cc (renamed from common_audio/wav_writer_unittest.cc)34
-rw-r--r--common_audio/wav_header.cc134
-rw-r--r--common_audio/wav_header.h16
-rw-r--r--common_audio/wav_header_unittest.cc92
-rw-r--r--common_audio/wav_writer.cc115
-rw-r--r--common_audio/wav_writer.h72
10 files changed, 506 insertions, 231 deletions
diff --git a/common_audio/BUILD.gn b/common_audio/BUILD.gn
index 1338a756..ba1d1795 100644
--- a/common_audio/BUILD.gn
+++ b/common_audio/BUILD.gn
@@ -85,8 +85,8 @@ source_set("common_audio") {
"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 ca77ba8f..8f96674f 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 00000000..685f3537
--- /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 00000000..c6c5d6b7
--- /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
index 9c593be6..1bdb655d 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 @@ TEST(WavWriterTest, CPP) {
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 @@ TEST(WavWriterTest, CPP) {
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 @@ TEST(WavWriterTest, LargeFile) {
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 @@ TEST(WavWriterTest, LargeFile) {
}
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 ce43896f..3182b1ff 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 @@ struct ChunkHeader {
};
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 inline void WriteFourCC(uint32_t* f, char a, char b, char c, char d) {
| 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));
-
- 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);
+ if (!CheckWavParameters(num_channels, sample_rate, format,
+ bytes_per_sample, num_samples))
+ return false;
+ 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 @@ void WriteWavHeader(uint8_t* buf,
// 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 f9ed8a57..0901f970 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 @@ bool CheckWavParameters(int num_channels,
// 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 f05160ea..ac79cc16 100644
--- a/common_audio/wav_header_unittest.cc
+++ b/common_audio/wav_header_unittest.cc
@@ -48,13 +48,85 @@ TEST(WavHeaderTest, CheckWavParameters) {
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 @@ TEST(WavHeaderTest, WriteWavHeader) {
};
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 52449789..00000000
--- 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 09667279..00000000
--- 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_