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@7585 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
andrew@webrtc.org 2014-10-31 21:51:03 +00:00
parent c2c94a9a9f
commit a3ed713dad
15 changed files with 537 additions and 262 deletions

View File

@ -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",
]

View File

@ -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': [

View File

@ -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();
}

View File

@ -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_

View File

@ -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));
}
}

View File

@ -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

View File

@ -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_

View File

@ -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);
}

View File

@ -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();
}

View File

@ -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_

View File

@ -1351,7 +1351,7 @@ int WebRtcAec_FreeAec(AecCore* aec) {
#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,

View File

@ -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 @@ struct AecCore {
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
};

View File

@ -489,7 +489,7 @@ void void_main(int argc, char* argv[]) {
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 @@ void void_main(int argc, char* argv[]) {
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 @@ void void_main(int argc, char* argv[]) {
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 @@ void void_main(int argc, char* argv[]) {
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,

View File

@ -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 @@ class RawFile {
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 WriteIntData(const int16_t* data,
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]);

View File

@ -73,9 +73,9 @@ int do_main(int argc, char* argv[]) {
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 @@ int do_main(int argc, char* argv[]) {
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));
}
}
}