| Index: media/base/sinc_resampler_unittest.cc
|
| diff --git a/media/base/sinc_resampler_unittest.cc b/media/base/sinc_resampler_unittest.cc
|
| index 8b89a5d3808034ab4d5ad705b8f0c6fd311cde41..b228f3d419a049320ce6e00285ab5816e0c9181b 100644
|
| --- a/media/base/sinc_resampler_unittest.cc
|
| +++ b/media/base/sinc_resampler_unittest.cc
|
| @@ -1,39 +1,42 @@
|
| -// 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/cpu.h"
|
| -#include "base/logging.h"
|
| -#include "base/strings/string_number_conversions.h"
|
| -#include "base/strings/stringize_macros.h"
|
| -#include "base/time/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/common_audio/resampler/sinusoidal_linear_chirp_source.h"
|
| +#include "webrtc/system_wrappers/interface/cpu_features_wrapper.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(int frames, float* destination));
|
| + MOCK_METHOD2(Run, void(int frames, float* destination));
|
| };
|
|
|
| ACTION(ClearBuffer) {
|
| @@ -54,22 +57,21 @@ 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, SincResampler::kDefaultRequestSize,
|
| - base::Bind(&MockSource::ProvideInput, base::Unretained(&mock_source)));
|
| + SincResampler resampler(kSampleRateRatio, SincResampler::kDefaultRequestSize,
|
| + &mock_source);
|
|
|
| static const int kChunks = 2;
|
| int max_chunk_size = resampler.ChunkSize() * kChunks;
|
| - scoped_ptr<float[]> resampled_destination(new float[max_chunk_size]);
|
| + 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(resampler.ChunkSize(), resampled_destination.get());
|
|
|
| // 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(max_chunk_size, resampled_destination.get());
|
| }
|
| @@ -77,13 +79,12 @@ TEST(SincResamplerTest, ChunkedResample) {
|
| // Test flush resets the internal state properly.
|
| TEST(SincResamplerTest, Flush) {
|
| MockSource mock_source;
|
| - SincResampler resampler(
|
| - kSampleRateRatio, SincResampler::kDefaultRequestSize,
|
| - base::Bind(&MockSource::ProvideInput, base::Unretained(&mock_source)));
|
| - scoped_ptr<float[]> resampled_destination(new float[resampler.ChunkSize()]);
|
| + SincResampler resampler(kSampleRateRatio, SincResampler::kDefaultRequestSize,
|
| + &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(resampler.ChunkSize() / 2, resampled_destination.get());
|
| ASSERT_NE(resampled_destination[0], 0);
|
| @@ -91,7 +92,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(resampler.ChunkSize() / 2, resampled_destination.get());
|
| for (int i = 0; i < resampler.ChunkSize() / 2; ++i)
|
| @@ -101,23 +102,21 @@ TEST(SincResamplerTest, Flush) {
|
| // Test flush resets the internal state properly.
|
| TEST(SincResamplerTest, DISABLED_SetRatioBench) {
|
| MockSource mock_source;
|
| - SincResampler resampler(
|
| - kSampleRateRatio, SincResampler::kDefaultRequestSize,
|
| - base::Bind(&MockSource::ProvideInput, base::Unretained(&mock_source)));
|
| + SincResampler resampler(kSampleRateRatio, SincResampler::kDefaultRequestSize,
|
| + &mock_source);
|
|
|
| - base::TimeTicks start = base::TimeTicks::HighResNow();
|
| + TickTime start = TickTime::Now();
|
| for (int i = 1; i < 10000; ++i)
|
| resampler.SetRatio(1.0 / i);
|
| - double total_time_c_ms =
|
| - (base::TimeTicks::HighResNow() - start).InMillisecondsF();
|
| - printf("SetRatio() took %.2fms.\n", total_time_c_ms);
|
| + double total_time_c_us = (TickTime::Now() - start).Microseconds();
|
| + printf("SetRatio() took %.2fms.\n", total_time_c_us / 1000);
|
| }
|
|
|
|
|
| // Define platform independent function name for Convolve* tests.
|
| -#if defined(ARCH_CPU_X86_FAMILY)
|
| +#if defined(WEBRTC_ARCH_X86_FAMILY)
|
| #define CONVOLVE_FUNC Convolve_SSE
|
| -#elif defined(ARCH_CPU_ARM_FAMILY) && defined(USE_NEON)
|
| +#elif defined(WEBRTC_ARCH_ARM_V7)
|
| #define CONVOLVE_FUNC Convolve_NEON
|
| #endif
|
|
|
| @@ -126,15 +125,16 @@ TEST(SincResamplerTest, DISABLED_SetRatioBench) {
|
| // will be tested by the parameterized SincResampler tests below.
|
| #if defined(CONVOLVE_FUNC)
|
| TEST(SincResamplerTest, Convolve) {
|
| -#if defined(ARCH_CPU_X86_FAMILY)
|
| - ASSERT_TRUE(base::CPU().has_sse());
|
| +#if defined(WEBRTC_ARCH_X86_FAMILY)
|
| + ASSERT_TRUE(WebRtc_GetCPUInfo(kSSE2));
|
| +#elif defined(WEBRTC_ARCH_ARM_V7)
|
| + ASSERT_TRUE(WebRtc_GetCPUFeaturesARM() & kCPUFeatureNEON);
|
| #endif
|
|
|
| // Initialize a dummy resampler.
|
| MockSource mock_source;
|
| - SincResampler resampler(
|
| - kSampleRateRatio, SincResampler::kDefaultRequestSize,
|
| - base::Bind(&MockSource::ProvideInput, base::Unretained(&mock_source)));
|
| + SincResampler resampler(kSampleRateRatio, SincResampler::kDefaultRequestSize,
|
| + &mock_source);
|
|
|
| // The optimized Convolve methods are slightly more precise than Convolve_C(),
|
| // so comparison must be done using an epsilon.
|
| @@ -167,121 +167,65 @@ TEST(SincResamplerTest, Convolve) {
|
| TEST(SincResamplerTest, ConvolveBenchmark) {
|
| // Initialize a dummy resampler.
|
| MockSource mock_source;
|
| - SincResampler resampler(
|
| - kSampleRateRatio, SincResampler::kDefaultRequestSize,
|
| - base::Bind(&MockSource::ProvideInput, base::Unretained(&mock_source)));
|
| + SincResampler resampler(kSampleRateRatio, SincResampler::kDefaultRequestSize,
|
| + &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)
|
| -#if defined(ARCH_CPU_X86_FAMILY)
|
| - ASSERT_TRUE(base::CPU().has_sse());
|
| +#if defined(WEBRTC_ARCH_X86_FAMILY)
|
| + ASSERT_TRUE(WebRtc_GetCPUInfo(kSSE2));
|
| +#elif defined(WEBRTC_ARCH_ARM_V7)
|
| + ASSERT_TRUE(WebRtc_GetCPUFeaturesARM() & kCPUFeatureNEON);
|
| #endif
|
|
|
| // 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();
|
| - 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);
|
| + 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_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
|
| }
|
|
|
| #undef CONVOLVE_FUNC
|
|
|
| -// 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 {
|
| - public:
|
| - SinusoidalLinearChirpSource(int sample_rate,
|
| - int samples,
|
| - double max_frequency)
|
| - : sample_rate_(sample_rate),
|
| - total_samples_(samples),
|
| - max_frequency_(max_frequency),
|
| - current_index_(0) {
|
| - // Chirp rate.
|
| - double duration = static_cast<double>(total_samples_) / sample_rate_;
|
| - k_ = (max_frequency_ - kMinFrequency) / duration;
|
| - }
|
| -
|
| - virtual ~SinusoidalLinearChirpSource() {}
|
| -
|
| - void ProvideInput(int frames, float* destination) {
|
| - for (int i = 0; i < frames; ++i, ++current_index_) {
|
| - // Filter out frequencies higher than Nyquist.
|
| - if (Frequency(current_index_) > 0.5 * sample_rate_) {
|
| - destination[i] = 0;
|
| - } else {
|
| - // Calculate time in seconds.
|
| - double t = static_cast<double>(current_index_) / sample_rate_;
|
| -
|
| - // Sinusoidal linear chirp.
|
| - destination[i] = sin(2 * M_PI * (kMinFrequency * t + (k_ / 2) * t * t));
|
| - }
|
| - }
|
| - }
|
| -
|
| - double Frequency(int position) {
|
| - return kMinFrequency + position * (max_frequency_ - kMinFrequency)
|
| - / total_samples_;
|
| - }
|
| -
|
| - private:
|
| - enum {
|
| - kMinFrequency = 5
|
| - };
|
| -
|
| - double sample_rate_;
|
| - int total_samples_;
|
| - double max_frequency_;
|
| - double k_;
|
| - int current_index_;
|
| -
|
| - DISALLOW_COPY_AND_ASSIGN(SinusoidalLinearChirpSource);
|
| -};
|
| -
|
| typedef std::tr1::tuple<int, int, double, double> SincResamplerTestData;
|
| class SincResamplerTest
|
| : public testing::TestWithParam<SincResamplerTestData> {
|
| @@ -306,25 +250,23 @@ class SincResamplerTest
|
| TEST_P(SincResamplerTest, Resample) {
|
| // Make comparisons using one second of data.
|
| static const double kTestDurationSecs = 1;
|
| - int input_samples = kTestDurationSecs * input_rate_;
|
| - int output_samples = kTestDurationSecs * output_rate_;
|
| + const int input_samples = kTestDurationSecs * input_rate_;
|
| + const int output_samples = kTestDurationSecs * output_rate_;
|
|
|
| // Nyquist frequency for the input sampling rate.
|
| - double input_nyquist_freq = 0.5 * input_rate_;
|
| + const double input_nyquist_freq = 0.5 * input_rate_;
|
|
|
| // Source for data to be resampled.
|
| SinusoidalLinearChirpSource resampler_source(
|
| - input_rate_, input_samples, input_nyquist_freq);
|
| + input_rate_, input_samples, input_nyquist_freq, 0);
|
|
|
| const double io_ratio = input_rate_ / static_cast<double>(output_rate_);
|
| - SincResampler resampler(
|
| - io_ratio, SincResampler::kDefaultRequestSize,
|
| - base::Bind(&SinusoidalLinearChirpSource::ProvideInput,
|
| - base::Unretained(&resampler_source)));
|
| + SincResampler resampler(io_ratio, SincResampler::kDefaultRequestSize,
|
| + &resampler_source);
|
|
|
| // Force an update to the sample rate ratio to ensure dyanmic sample rate
|
| // changes are working correctly.
|
| - scoped_ptr<float[]> kernel(new float[SincResampler::kKernelStorageSize]);
|
| + scoped_array<float> kernel(new float[SincResampler::kKernelStorageSize]);
|
| memcpy(kernel.get(), resampler.get_kernel_for_testing(),
|
| SincResampler::kKernelStorageSize);
|
| resampler.SetRatio(M_PI);
|
| @@ -336,16 +278,16 @@ TEST_P(SincResamplerTest, Resample) {
|
|
|
| // 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.
|
| - scoped_ptr<float[]> resampled_destination(new float[output_samples]);
|
| - scoped_ptr<float[]> pure_destination(new float[output_samples]);
|
| + scoped_array<float> resampled_destination(new float[output_samples]);
|
| + scoped_array<float> pure_destination(new float[output_samples]);
|
|
|
| // Generate resampled signal.
|
| resampler.Resample(output_samples, resampled_destination.get());
|
|
|
| // Generate pure signal.
|
| SinusoidalLinearChirpSource pure_source(
|
| - output_rate_, output_samples, input_nyquist_freq);
|
| - pure_source.ProvideInput(output_samples, pure_destination.get());
|
| + output_rate_, output_samples, input_nyquist_freq, 0);
|
| + pure_source.Run(output_samples, pure_destination.get());
|
|
|
| // Range of the Nyquist frequency (0.5 * min(input rate, output_rate)) which
|
| // we refer to as low and high.
|
| @@ -441,4 +383,4 @@ 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
|
|
|