Modify SincResampler to build in webrtc.

This is the first in a series of CLs to bring arbitrary resampling to webrtc.

* Replace Chromium-specific helpers with their respective webrtc versions.
* Add a second constructor to permit runtime selection of block_size.
* Add stringize_macros to system_wrappers.

BUG=webrtc:1395
TESTED=unit tests

Review URL: https://webrtc-codereview.appspot.com/1097012

git-svn-id: http://webrtc.googlecode.com/svn/trunk@3518 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
andrew@webrtc.org 2013-02-15 03:54:22 +00:00
parent 6f6acd9f80
commit 076fc12539
7 changed files with 256 additions and 189 deletions

View File

@ -25,6 +25,8 @@
'sources': [
'include/resampler.h',
'resampler.cc',
'sinc_resampler.cc',
'sinc_resampler.h',
],
},
], # targets
@ -37,10 +39,12 @@
'dependencies': [
'resampler',
'<(webrtc_root)/test/test.gyp:test_support_main',
'<(DEPTH)/testing/gmock.gyp:gmock',
'<(DEPTH)/testing/gtest.gyp:gtest',
],
'sources': [
'resampler_unittest.cc',
'sinc_resampler_unittest.cc',
],
}, # resampler_unittests
], # targets
@ -48,8 +52,3 @@
], # conditions
}
# Local Variables:
# tab-width:2
# indent-tabs-mode:nil
# End:
# vim: set expandtab tabstop=2 shiftwidth=2:

View File

@ -1,7 +1,16 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
/*
* Copyright (c) 2013 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.
*/
// Modified from the Chromium original:
// src/media/base/sinc_resampler.cc
// Input buffer layout, dividing the total buffer into regions (r0_ - r5_):
//
// |----------------|-----------------------------------------|----------------|
@ -34,23 +43,24 @@
// MSVC++ requires this to be set before any other includes to get M_PI.
#define _USE_MATH_DEFINES
#include "media/base/sinc_resampler.h"
#include "webrtc/common_audio/resampler/sinc_resampler.h"
#include "webrtc/system_wrappers/interface/compile_assert.h"
#include "webrtc/system_wrappers/interface/cpu_features_wrapper.h"
#include "webrtc/typedefs.h"
#include <cmath>
#include <cstring>
#include "base/cpu.h"
#include "base/logging.h"
#include "build/build_config.h"
#if defined(ARCH_CPU_X86_FAMILY) && defined(__SSE__)
#if defined(WEBRTC_USE_SSE2)
#include <xmmintrin.h>
#endif
#if defined(ARCH_CPU_ARM_FAMILY) && defined(USE_NEON)
#include <arm_neon.h>
#endif
// TODO(ajm): See note below in Convolve_NEON.
//#if defined(WEBRTC_ARCH_ARM_NEON) || defined(WEBRTC_DETECT_ARM_NEON)
//#include <arm_neon.h>
//#endif
namespace media {
namespace webrtc {
namespace {
@ -63,7 +73,7 @@ enum {
// The number of destination frames generated per processing pass. Affects
// how often and for how much SincResampler calls back for input. Must be
// greater than kKernelSize.
kBlockSize = 512,
kDefaultBlockSize = 512,
// The kernel offset count is used for interpolation and is the number of
// sub-sample kernel shifts. Can be adjusted for quality (higher is better)
@ -72,59 +82,87 @@ enum {
kKernelStorageSize = kKernelSize * (kKernelOffsetCount + 1),
// The size (in samples) of the internal buffer used by the resampler.
kBufferSize = kBlockSize + kKernelSize
kDefaultBufferSize = kDefaultBlockSize + kKernelSize
};
} // namespace
const int SincResampler::kMaximumLookAheadSize = kBufferSize;
SincResampler::SincResampler(double io_sample_rate_ratio, const ReadCB& read_cb)
SincResampler::SincResampler(double io_sample_rate_ratio,
SincResamplerCallback* read_cb,
int block_size)
: io_sample_rate_ratio_(io_sample_rate_ratio),
virtual_source_idx_(0),
buffer_primed_(false),
read_cb_(read_cb),
block_size_(block_size),
buffer_size_(block_size_ + kKernelSize),
// Create input buffers with a 16-byte alignment for SSE optimizations.
kernel_storage_(static_cast<float*>(
base::AlignedAlloc(sizeof(float) * kKernelStorageSize, 16))),
AlignedMalloc(sizeof(float) * kKernelStorageSize, 16))),
input_buffer_(static_cast<float*>(
base::AlignedAlloc(sizeof(float) * kBufferSize, 16))),
AlignedMalloc(sizeof(float) * buffer_size_, 16))),
// Setup various region pointers in the buffer (see diagram above).
r0_(input_buffer_.get() + kKernelSize / 2),
r1_(input_buffer_.get()),
r2_(r0_),
r3_(r0_ + kBlockSize - kKernelSize / 2),
r4_(r0_ + kBlockSize),
r3_(r0_ + block_size_ - kKernelSize / 2),
r4_(r0_ + block_size_),
r5_(r0_ + kKernelSize / 2) {
// Ensure kKernelSize is a multiple of 32 for easy SSE optimizations; causes
// r0_ and r5_ (used for input) to always be 16-byte aligned by virtue of
// input_buffer_ being 16-byte aligned.
DCHECK_EQ(kKernelSize % 32, 0) << "kKernelSize must be a multiple of 32!";
DCHECK_GT(kBlockSize, kKernelSize)
<< "kBlockSize must be greater than kKernelSize!";
// Basic sanity checks to ensure buffer regions are laid out correctly:
// r0_ and r2_ should always be the same position.
DCHECK_EQ(r0_, r2_);
// r1_ at the beginning of the buffer.
DCHECK_EQ(r1_, input_buffer_.get());
// r1_ left of r2_, r2_ left of r5_ and r1_, r2_ size correct.
DCHECK_EQ(r2_ - r1_, r5_ - r2_);
// r3_ left of r4_, r5_ left of r0_ and r3_ size correct.
DCHECK_EQ(r4_ - r3_, r5_ - r0_);
// r3_, r4_ size correct and r4_ at the end of the buffer.
DCHECK_EQ(r4_ + (r4_ - r3_), r1_ + kBufferSize);
// r5_ size correct and at the end of the buffer.
DCHECK_EQ(r5_ + kBlockSize, r1_ + kBufferSize);
memset(kernel_storage_.get(), 0,
sizeof(*kernel_storage_.get()) * kKernelStorageSize);
memset(input_buffer_.get(), 0, sizeof(*input_buffer_.get()) * kBufferSize);
Initialize();
InitializeKernel();
}
SincResampler::SincResampler(double io_sample_rate_ratio,
SincResamplerCallback* read_cb)
: io_sample_rate_ratio_(io_sample_rate_ratio),
virtual_source_idx_(0),
buffer_primed_(false),
read_cb_(read_cb),
block_size_(kDefaultBlockSize),
buffer_size_(kDefaultBufferSize),
// Create input buffers with a 16-byte alignment for SSE optimizations.
kernel_storage_(static_cast<float*>(
AlignedMalloc(sizeof(float) * kKernelStorageSize, 16))),
input_buffer_(static_cast<float*>(
AlignedMalloc(sizeof(float) * buffer_size_, 16))),
// Setup various region pointers in the buffer (see diagram above).
r0_(input_buffer_.get() + kKernelSize / 2),
r1_(input_buffer_.get()),
r2_(r0_),
r3_(r0_ + block_size_ - kKernelSize / 2),
r4_(r0_ + block_size_),
r5_(r0_ + kKernelSize / 2) {
Initialize();
InitializeKernel();
}
SincResampler::~SincResampler() {}
void SincResampler::Initialize() {
// Ensure kKernelSize is a multiple of 32 for easy SSE optimizations; causes
// r0_ and r5_ (used for input) to always be 16-byte aligned by virtue of
// input_buffer_ being 16-byte aligned.
COMPILE_ASSERT(kKernelSize % 32 == 0);
assert(block_size_ > kKernelSize);
// Basic sanity checks to ensure buffer regions are laid out correctly:
// r0_ and r2_ should always be the same position.
assert(r0_ == r2_);
// r1_ at the beginning of the buffer.
assert(r1_ == input_buffer_.get());
// r1_ left of r2_, r2_ left of r5_ and r1_, r2_ size correct.
assert(r2_ - r1_ == r5_ - r2_);
// r3_ left of r4_, r5_ left of r0_ and r3_ size correct.
assert(r4_ - r3_ == r5_ - r0_);
// r3_, r4_ size correct and r4_ at the end of the buffer.
assert(r4_ + (r4_ - r3_) == r1_ + buffer_size_);
// r5_ size correct and at the end of the buffer.
assert(r5_ + block_size_ == r1_ + buffer_size_);
memset(kernel_storage_.get(), 0,
sizeof(*kernel_storage_.get()) * kKernelStorageSize);
memset(input_buffer_.get(), 0, sizeof(*input_buffer_.get()) * buffer_size_);
}
void SincResampler::InitializeKernel() {
// Blackman window parameters.
static const double kAlpha = 0.16;
@ -173,13 +211,13 @@ void SincResampler::Resample(float* destination, int frames) {
// Step (1) -- Prime the input buffer at the start of the input stream.
if (!buffer_primed_) {
read_cb_.Run(r0_, kBlockSize + kKernelSize / 2);
read_cb_->Run(r0_, block_size_ + kKernelSize / 2);
buffer_primed_ = true;
}
// Step (2) -- Resample!
while (remaining_frames) {
while (virtual_source_idx_ < kBlockSize) {
while (virtual_source_idx_ < block_size_) {
// |virtual_source_idx_| lies in between two kernel offsets so figure out
// what they are.
int source_idx = static_cast<int>(virtual_source_idx_);
@ -209,7 +247,7 @@ void SincResampler::Resample(float* destination, int frames) {
}
// Wrap back around to the start.
virtual_source_idx_ -= kBlockSize;
virtual_source_idx_ -= block_size_;
// Step (3) Copy r3_ to r1_ and r4_ to r2_.
// This wraps the last input frames back to the start of the buffer.
@ -218,18 +256,18 @@ void SincResampler::Resample(float* destination, int frames) {
// Step (4)
// Refresh the buffer with more input.
read_cb_.Run(r5_, kBlockSize);
read_cb_->Run(r5_, block_size_);
}
}
int SincResampler::ChunkSize() {
return kBlockSize / io_sample_rate_ratio_;
return block_size_ / io_sample_rate_ratio_;
}
void SincResampler::Flush() {
virtual_source_idx_ = 0;
buffer_primed_ = false;
memset(input_buffer_.get(), 0, sizeof(*input_buffer_.get()) * kBufferSize);
memset(input_buffer_.get(), 0, sizeof(*input_buffer_.get()) * buffer_size_);
}
float SincResampler::Convolve(const float* input_ptr, const float* k1,
@ -240,11 +278,15 @@ float SincResampler::Convolve(const float* input_ptr, const float* k1,
typedef float (*ConvolveProc)(const float* src, const float* k1,
const float* k2,
double kernel_interpolation_factor);
#if defined(ARCH_CPU_X86_FAMILY) && defined(__SSE__)
#if defined(WEBRTC_USE_SSE2)
static const ConvolveProc kConvolveProc =
base::CPU().has_sse() ? Convolve_SSE : Convolve_C;
#elif defined(ARCH_CPU_ARM_FAMILY) && defined(USE_NEON)
WebRtc_GetCPUInfo(kSSE2) ? Convolve_SSE : Convolve_C;
#elif defined(WEBRTC_ARCH_ARM_NEON)
static const ConvolveProc kConvolveProc = Convolve_NEON;
#elif defined(WEBRTC_DETECT_ARM_NEON)
static const ConvolveProc kConvolveProc =
WebRtc_GetCPUFeaturesARM() & kCPUFeatureNEON ? Convolve_NEON :
Convolve_C;
#else
static const ConvolveProc kConvolveProc = Convolve_C;
#endif
@ -271,14 +313,14 @@ float SincResampler::Convolve_C(const float* input_ptr, const float* k1,
+ kernel_interpolation_factor * sum2;
}
#if defined(ARCH_CPU_X86_FAMILY) && defined(__SSE__)
#if defined(WEBRTC_USE_SSE2)
float SincResampler::Convolve_SSE(const float* input_ptr, const float* k1,
const float* k2,
double kernel_interpolation_factor) {
// Ensure |k1|, |k2| are 16-byte aligned for SSE usage. Should always be true
// so long as kKernelSize is a multiple of 16.
DCHECK_EQ(0u, reinterpret_cast<uintptr_t>(k1) & 0x0F);
DCHECK_EQ(0u, reinterpret_cast<uintptr_t>(k2) & 0x0F);
assert(0u == (reinterpret_cast<uintptr_t>(k1) & 0x0F));
assert(0u == (reinterpret_cast<uintptr_t>(k2) & 0x0F));
__m128 m_input;
__m128 m_sums1 = _mm_setzero_ps();
@ -315,33 +357,36 @@ float SincResampler::Convolve_SSE(const float* input_ptr, const float* k1,
}
#endif
#if defined(ARCH_CPU_ARM_FAMILY) && defined(USE_NEON)
#if defined(WEBRTC_ARCH_ARM_NEON) || defined(WEBRTC_DETECT_ARM_NEON)
float SincResampler::Convolve_NEON(const float* input_ptr, const float* k1,
const float* k2,
double kernel_interpolation_factor) {
float32x4_t m_input;
float32x4_t m_sums1 = vmovq_n_f32(0);
float32x4_t m_sums2 = vmovq_n_f32(0);
// TODO(ajm): The AndroidNDK bot is giving compile errors in this function.
// Fallback to the plain C version until it's resolved.
return Convolve_C(input_ptr, k1, k2, kernel_interpolation_factor);
//float32x4_t m_input;
//float32x4_t m_sums1 = vmovq_n_f32(0);
//float32x4_t m_sums2 = vmovq_n_f32(0);
const float* upper = input_ptr + kKernelSize;
for (; input_ptr < upper; ) {
m_input = vld1q_f32(input_ptr);
input_ptr += 4;
m_sums1 = vmlaq_f32(m_sums1, m_input, vld1q_f32(k1));
k1 += 4;
m_sums2 = vmlaq_f32(m_sums2, m_input, vld1q_f32(k2));
k2 += 4;
}
//const float* upper = input_ptr + kKernelSize;
//for (; input_ptr < upper; ) {
// m_input = vld1q_f32(input_ptr);
// input_ptr += 4;
// m_sums1 = vmlaq_f32(m_sums1, m_input, vld1q_f32(k1));
// k1 += 4;
// m_sums2 = vmlaq_f32(m_sums2, m_input, vld1q_f32(k2));
// k2 += 4;
//}
// Linearly interpolate the two "convolutions".
m_sums1 = vmlaq_f32(
vmulq_f32(m_sums1, vmovq_n_f32(1.0 - kernel_interpolation_factor)),
m_sums2, vmovq_n_f32(kernel_interpolation_factor));
//m_sums1 = vmlaq_f32(
// vmulq_f32(m_sums1, vmovq_n_f32(1.0 - kernel_interpolation_factor)),
// m_sums2, vmovq_n_f32(kernel_interpolation_factor));
// Sum components together.
float32x2_t m_half = vadd_f32(vget_high_f32(m_sums1), vget_low_f32(m_sums1));
return vget_lane_f32(vpadd_f32(m_half, m_half), 0);
//float32x2_t m_half = vadd_f32(vget_high_f32(m_sums1), vget_low_f32(m_sums1));
//return vget_lane_f32(vpadd_f32(m_half, m_half), 0);
}
#endif
} // namespace media
} // namespace webrtc

View File

@ -1,34 +1,45 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/*
* Copyright (c) 2013 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 MEDIA_BASE_SINC_RESAMPLER_H_
#define MEDIA_BASE_SINC_RESAMPLER_H_
// Modified from the Chromium original here:
// src/media/base/sinc_resampler.h
#include "base/callback.h"
#include "base/gtest_prod_util.h"
#include "base/memory/aligned_memory.h"
#include "base/memory/scoped_ptr.h"
#include "media/base/media_export.h"
#ifndef WEBRTC_COMMON_AUDIO_RESAMPLER_SINC_RESAMPLER_H_
#define WEBRTC_COMMON_AUDIO_RESAMPLER_SINC_RESAMPLER_H_
namespace media {
#include "webrtc/system_wrappers/interface/aligned_malloc.h"
#include "webrtc/system_wrappers/interface/constructor_magic.h"
#include "webrtc/system_wrappers/interface/scoped_ptr.h"
#include "webrtc/test/testsupport/gtest_prod_util.h"
namespace webrtc {
// Callback class to provide SincResampler with input.
class SincResamplerCallback {
public:
virtual ~SincResamplerCallback() {}
virtual void Run(float* destination, int frames) = 0;
};
// SincResampler is a high-quality single-channel sample-rate converter.
class MEDIA_EXPORT SincResampler {
class SincResampler {
public:
// The maximum number of samples that may be requested from the callback ahead
// of the current position in the stream.
static const int kMaximumLookAheadSize;
// Callback type for providing more data into the resampler. Expects |frames|
// of data to be rendered into |destination|; zero padded if not enough frames
// are available to satisfy the request.
typedef base::Callback<void(float* destination, int frames)> ReadCB;
// Constructs a SincResampler with the specified |read_cb|, which is used to
// acquire audio data for resampling. |io_sample_rate_ratio| is the ratio of
// input / output sample rates.
SincResampler(double io_sample_rate_ratio, const ReadCB& read_cb);
// input / output sample rates. If desired, the number of destination frames
// generated per processing pass can be specified through |block_size|.
SincResampler(double io_sample_rate_ratio,
SincResamplerCallback* read_cb);
SincResampler(double io_sample_rate_ratio,
SincResamplerCallback* read_cb,
int block_size);
virtual ~SincResampler();
// Resample |frames| of data from |read_cb_| into |destination|.
@ -45,6 +56,7 @@ class MEDIA_EXPORT SincResampler {
FRIEND_TEST_ALL_PREFIXES(SincResamplerTest, Convolve);
FRIEND_TEST_ALL_PREFIXES(SincResamplerTest, ConvolveBenchmark);
void Initialize();
void InitializeKernel();
// Compute convolution of |k1| and |k2| over |input_ptr|, resultant sums are
@ -73,15 +85,21 @@ class MEDIA_EXPORT SincResampler {
bool buffer_primed_;
// Source of data for resampling.
ReadCB read_cb_;
SincResamplerCallback* read_cb_;
// See kDefaultBlockSize.
int block_size_;
// See kDefaultBufferSize.
int buffer_size_;
// Contains kKernelOffsetCount kernels back-to-back, each of size kKernelSize.
// The kernel offsets are sub-sample shifts of a windowed sinc shifted from
// 0.0 to 1.0 sample.
scoped_ptr_malloc<float, base::ScopedPtrAlignedFree> kernel_storage_;
scoped_ptr_malloc<float, AlignedFree> kernel_storage_;
// Data from the source is copied into this buffer for each processing pass.
scoped_ptr_malloc<float, base::ScopedPtrAlignedFree> input_buffer_;
scoped_ptr_malloc<float, AlignedFree> input_buffer_;
// Pointers to the various regions inside |input_buffer_|. See the diagram at
// the top of the .cc file for more information.
@ -95,6 +113,6 @@ class MEDIA_EXPORT SincResampler {
DISALLOW_COPY_AND_ASSIGN(SincResampler);
};
} // namespace media
} // namespace webrtc
#endif // MEDIA_BASE_SINC_RESAMPLER_H_
#endif // WEBRTC_COMMON_AUDIO_RESAMPLER_SINC_RESAMPLER_H_

View File

@ -1,38 +1,40 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/*
* Copyright (c) 2013 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.
*/
// Modified from the Chromium original:
// src/media/base/sinc_resampler_unittest.cc
// MSVC++ requires this to be set before any other includes to get M_PI.
#define _USE_MATH_DEFINES
#include <cmath>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/command_line.h"
#include "base/logging.h"
#include "base/string_number_conversions.h"
#include "base/strings/stringize_macros.h"
#include "base/time.h"
#include "build/build_config.h"
#include "media/base/sinc_resampler.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "webrtc/common_audio/resampler/sinc_resampler.h"
#include "webrtc/system_wrappers/interface/scoped_ptr.h"
#include "webrtc/system_wrappers/interface/stringize_macros.h"
#include "webrtc/system_wrappers/interface/tick_util.h"
#include "webrtc/test/test_suite.h"
using testing::_;
namespace media {
namespace webrtc {
static const double kSampleRateRatio = 192000.0 / 44100.0;
static const double kKernelInterpolationFactor = 0.5;
// Command line switch for runtime adjustment of ConvolveBenchmark iterations.
static const char kConvolveIterations[] = "convolve-iterations";
// Helper class to ensure ChunkedResample() functions properly.
class MockSource {
class MockSource : public SincResamplerCallback {
public:
MOCK_METHOD2(ProvideInput, void(float* destination, int frames));
MOCK_METHOD2(Run, void(float* destination, int frames));
};
ACTION(ClearBuffer) {
@ -53,22 +55,20 @@ TEST(SincResamplerTest, ChunkedResample) {
// Choose a high ratio of input to output samples which will result in quick
// exhaustion of SincResampler's internal buffers.
SincResampler resampler(
kSampleRateRatio,
base::Bind(&MockSource::ProvideInput, base::Unretained(&mock_source)));
SincResampler resampler(kSampleRateRatio, &mock_source);
static const int kChunks = 2;
int max_chunk_size = resampler.ChunkSize() * kChunks;
scoped_array<float> resampled_destination(new float[max_chunk_size]);
// Verify requesting ChunkSize() frames causes a single callback.
EXPECT_CALL(mock_source, ProvideInput(_, _))
EXPECT_CALL(mock_source, Run(_, _))
.Times(1).WillOnce(ClearBuffer());
resampler.Resample(resampled_destination.get(), resampler.ChunkSize());
// Verify requesting kChunks * ChunkSize() frames causes kChunks callbacks.
testing::Mock::VerifyAndClear(&mock_source);
EXPECT_CALL(mock_source, ProvideInput(_, _))
EXPECT_CALL(mock_source, Run(_, _))
.Times(kChunks).WillRepeatedly(ClearBuffer());
resampler.Resample(resampled_destination.get(), max_chunk_size);
}
@ -76,13 +76,11 @@ TEST(SincResamplerTest, ChunkedResample) {
// Test flush resets the internal state properly.
TEST(SincResamplerTest, Flush) {
MockSource mock_source;
SincResampler resampler(
kSampleRateRatio,
base::Bind(&MockSource::ProvideInput, base::Unretained(&mock_source)));
SincResampler resampler(kSampleRateRatio, &mock_source);
scoped_array<float> resampled_destination(new float[resampler.ChunkSize()]);
// Fill the resampler with junk data.
EXPECT_CALL(mock_source, ProvideInput(_, _))
EXPECT_CALL(mock_source, Run(_, _))
.Times(1).WillOnce(FillBuffer());
resampler.Resample(resampled_destination.get(), resampler.ChunkSize() / 2);
ASSERT_NE(resampled_destination[0], 0);
@ -90,7 +88,7 @@ TEST(SincResamplerTest, Flush) {
// Flush and request more data, which should all be zeros now.
resampler.Flush();
testing::Mock::VerifyAndClear(&mock_source);
EXPECT_CALL(mock_source, ProvideInput(_, _))
EXPECT_CALL(mock_source, Run(_, _))
.Times(1).WillOnce(ClearBuffer());
resampler.Resample(resampled_destination.get(), resampler.ChunkSize() / 2);
for (int i = 0; i < resampler.ChunkSize() / 2; ++i)
@ -98,9 +96,9 @@ TEST(SincResamplerTest, Flush) {
}
// Define platform independent function name for Convolve* tests.
#if defined(ARCH_CPU_X86_FAMILY) && defined(__SSE__)
#if defined(WEBRTC_USE_SSE2) && defined(__SSE__)
#define CONVOLVE_FUNC Convolve_SSE
#elif defined(ARCH_CPU_ARM_FAMILY) && defined(USE_NEON)
#elif defined(WEBRTC_ARCH_ARM_NEON) || defined(WEBRTC_DETECT_ARM_NEON)
#define CONVOLVE_FUNC Convolve_NEON
#endif
@ -111,9 +109,7 @@ TEST(SincResamplerTest, Flush) {
TEST(SincResamplerTest, Convolve) {
// Initialize a dummy resampler.
MockSource mock_source;
SincResampler resampler(
kSampleRateRatio,
base::Bind(&MockSource::ProvideInput, base::Unretained(&mock_source)));
SincResampler resampler(kSampleRateRatio, &mock_source);
// The optimized Convolve methods are slightly more precise than Convolve_C(),
// so comparison must be done using an epsilon.
@ -146,59 +142,53 @@ TEST(SincResamplerTest, Convolve) {
TEST(SincResamplerTest, ConvolveBenchmark) {
// Initialize a dummy resampler.
MockSource mock_source;
SincResampler resampler(
kSampleRateRatio,
base::Bind(&MockSource::ProvideInput, base::Unretained(&mock_source)));
SincResampler resampler(kSampleRateRatio, &mock_source);
// Retrieve benchmark iterations from command line.
int convolve_iterations = 10;
std::string iterations(CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
kConvolveIterations));
if (!iterations.empty())
base::StringToInt(iterations, &convolve_iterations);
// TODO(ajm): Reintroduce this as a command line option.
const int kConvolveIterations = 1000000;
printf("Benchmarking %d iterations:\n", convolve_iterations);
printf("Benchmarking %d iterations:\n", kConvolveIterations);
// Benchmark Convolve_C().
base::TimeTicks start = base::TimeTicks::HighResNow();
for (int i = 0; i < convolve_iterations; ++i) {
TickTime start = TickTime::Now();
for (int i = 0; i < kConvolveIterations; ++i) {
resampler.Convolve_C(
resampler.kernel_storage_.get(), resampler.kernel_storage_.get(),
resampler.kernel_storage_.get(), kKernelInterpolationFactor);
}
double total_time_c_ms =
(base::TimeTicks::HighResNow() - start).InMillisecondsF();
printf("Convolve_C took %.2fms.\n", total_time_c_ms);
double total_time_c_us = (TickTime::Now() - start).Microseconds();
printf("Convolve_C took %.2fms.\n", total_time_c_us / 1000);
#if defined(CONVOLVE_FUNC)
// Benchmark with unaligned input pointer.
start = base::TimeTicks::HighResNow();
for (int j = 0; j < convolve_iterations; ++j) {
start = TickTime::Now();
for (int j = 0; j < kConvolveIterations; ++j) {
resampler.CONVOLVE_FUNC(
resampler.kernel_storage_.get() + 1, resampler.kernel_storage_.get(),
resampler.kernel_storage_.get(), kKernelInterpolationFactor);
}
double total_time_optimized_unaligned_ms =
(base::TimeTicks::HighResNow() - start).InMillisecondsF();
double total_time_optimized_unaligned_us =
(TickTime::Now() - start).Microseconds();
printf(STRINGIZE(CONVOLVE_FUNC) "(unaligned) took %.2fms; which is %.2fx "
"faster than Convolve_C.\n", total_time_optimized_unaligned_ms,
total_time_c_ms / total_time_optimized_unaligned_ms);
"faster than Convolve_C.\n", total_time_optimized_unaligned_us / 1000,
total_time_c_us / total_time_optimized_unaligned_us);
// Benchmark with aligned input pointer.
start = base::TimeTicks::HighResNow();
for (int j = 0; j < convolve_iterations; ++j) {
start = TickTime::Now();
for (int j = 0; j < kConvolveIterations; ++j) {
resampler.CONVOLVE_FUNC(
resampler.kernel_storage_.get(), resampler.kernel_storage_.get(),
resampler.kernel_storage_.get(), kKernelInterpolationFactor);
}
double total_time_optimized_aligned_ms =
(base::TimeTicks::HighResNow() - start).InMillisecondsF();
double total_time_optimized_aligned_us =
(TickTime::Now() - start).Microseconds();
printf(STRINGIZE(CONVOLVE_FUNC) " (aligned) took %.2fms; which is %.2fx "
"faster than Convolve_C and %.2fx faster than "
STRINGIZE(CONVOLVE_FUNC) " (unaligned).\n",
total_time_optimized_aligned_ms,
total_time_c_ms / total_time_optimized_aligned_ms,
total_time_optimized_unaligned_ms / total_time_optimized_aligned_ms);
total_time_optimized_aligned_us / 1000,
total_time_c_us / total_time_optimized_aligned_us,
total_time_optimized_unaligned_us / total_time_optimized_aligned_us);
#endif
}
@ -207,7 +197,7 @@ TEST(SincResamplerTest, ConvolveBenchmark) {
// Fake audio source for testing the resampler. Generates a sinusoidal linear
// chirp (http://en.wikipedia.org/wiki/Chirp) which can be tuned to stress the
// resampler for the specific sample rate conversion being used.
class SinusoidalLinearChirpSource {
class SinusoidalLinearChirpSource : public SincResamplerCallback {
public:
SinusoidalLinearChirpSource(int sample_rate, int samples,
double max_frequency)
@ -222,7 +212,7 @@ class SinusoidalLinearChirpSource {
virtual ~SinusoidalLinearChirpSource() {}
void ProvideInput(float* destination, int frames) {
virtual void Run(float* destination, int frames) {
for (int i = 0; i < frames; ++i, ++current_index_) {
// Filter out frequencies higher than Nyquist.
if (Frequency(current_index_) > 0.5 * sample_rate_) {
@ -292,8 +282,7 @@ TEST_P(SincResamplerTest, Resample) {
SincResampler resampler(
input_rate_ / static_cast<double>(output_rate_),
base::Bind(&SinusoidalLinearChirpSource::ProvideInput,
base::Unretained(&resampler_source)));
&resampler_source);
// TODO(dalecurtis): If we switch to AVX/SSE optimization, we'll need to
// allocate these on 32-byte boundaries and ensure they're sized % 32 bytes.
@ -306,7 +295,7 @@ TEST_P(SincResamplerTest, Resample) {
// Generate pure signal.
SinusoidalLinearChirpSource pure_source(
output_rate_, output_samples, input_nyquist_freq);
pure_source.ProvideInput(pure_destination.get(), output_samples);
pure_source.Run(pure_destination.get(), output_samples);
// Range of the Nyquist frequency (0.5 * min(input rate, output_rate)) which
// we refer to as low and high.
@ -402,4 +391,5 @@ INSTANTIATE_TEST_CASE_P(
std::tr1::make_tuple(96000, 192000, kResamplingRMSError, -73.52),
std::tr1::make_tuple(192000, 192000, kResamplingRMSError, -73.52)));
} // namespace media
} // namespace webrtc

View File

@ -1,15 +1,22 @@
// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
/*
* Copyright (c) 2013 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.
*/
// Modified from the Chromium original:
// src/base/strings/stringize_macros.h
// This file defines preprocessor macros for stringizing preprocessor
// symbols (or their output) and manipulating preprocessor symbols
// that define strings.
#ifndef BASE_STRINGS_STRINGIZE_MACROS_H_
#define BASE_STRINGS_STRINGIZE_MACROS_H_
#include "build/build_config.h"
#ifndef WEBRTC_SYSTEM_WRAPPERS_INTERFACE_STRINGIZE_MACROS_H_
#define WEBRTC_SYSTEM_WRAPPERS_INTERFACE_STRINGIZE_MACROS_H_
// This is not very useful as it does not expand defined symbols if
// called directly. Use its counterpart without the _NO_EXPANSION
@ -28,4 +35,4 @@
// STRINGIZE(B(y)) produces "myobj->FunctionCall(y)"
#define STRINGIZE(x) STRINGIZE_NO_EXPANSION(x)
#endif // BASE_STRINGS_STRINGIZE_MACROS_H_
#endif // WEBRTC_SYSTEM_WRAPPERS_INTERFACE_STRINGIZE_MACROS_H_

View File

@ -1,8 +1,14 @@
// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/*
* Copyright (c) 2013 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 "base/strings/stringize_macros.h"
#include "webrtc/system_wrappers/interface/stringize_macros.h"
#include "testing/gtest/include/gtest/gtest.h"

View File

@ -48,6 +48,7 @@
'../interface/sleep.h',
'../interface/sort.h',
'../interface/static_instance.h',
'../interface/stringize_macros.h',
'../interface/thread_wrapper.h',
'../interface/tick_util.h',
'../interface/trace.h',
@ -258,6 +259,7 @@
'data_log_helpers_unittest.cc',
'data_log_c_helpers_unittest.c',
'data_log_c_helpers_unittest.h',
'stringize_macros_unittest.cc',
'thread_unittest.cc',
'thread_posix_unittest.cc',
'trace_unittest.cc',