diff options
author | Android Chromium Automerger <chromium-automerger@android> | 2014-08-21 15:51:42 +0000 |
---|---|---|
committer | Android Chromium Automerger <chromium-automerger@android> | 2014-08-21 15:51:42 +0000 |
commit | e0ff458bb4794d90c8fa9dd338d7e9517e139f77 (patch) | |
tree | 53a313a83221a24a3149bb6b3a8146236bc0dbb4 /common_audio | |
parent | 11a9b6df83dd129ef51af4e1b4f1058eed29d3e2 (diff) | |
parent | 67afd1fc176021f625e064f20ae747e23d87d727 (diff) | |
download | webrtc-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.gyp | 6 | ||||
-rw-r--r-- | common_audio/signal_processing/include/signal_processing_library.h | 18 | ||||
-rw-r--r-- | common_audio/signal_processing/include/spl_inl_armv7.h | 6 | ||||
-rw-r--r-- | common_audio/signal_processing/include/spl_inl_mips.h | 12 | ||||
-rw-r--r-- | common_audio/signal_processing/signal_processing_unittest.cc | 6 | ||||
-rw-r--r-- | common_audio/wav_header.cc | 152 | ||||
-rw-r--r-- | common_audio/wav_header.h | 46 | ||||
-rw-r--r-- | common_audio/wav_header_unittest.cc | 77 | ||||
-rw-r--r-- | common_audio/wav_writer.cc | 103 | ||||
-rw-r--r-- | common_audio/wav_writer.h | 65 | ||||
-rw-r--r-- | common_audio/wav_writer_unittest.cc | 124 |
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)); +} |