Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 // MSVC++ requires this to be set before any other includes to get M_PI. | 5 // MSVC++ requires this to be set before any other includes to get M_PI. |
| 6 #define _USE_MATH_DEFINES | 6 #define _USE_MATH_DEFINES |
| 7 | 7 |
| 8 #include <cmath> | 8 #include <cmath> |
| 9 | 9 |
| 10 #include "base/bind.h" | 10 #include "base/bind.h" |
| 11 #include "base/bind_helpers.h" | 11 #include "base/bind_helpers.h" |
| 12 #include "base/format_macros.h" | |
| 12 #include "base/logging.h" | 13 #include "base/logging.h" |
| 13 #include "base/memory/scoped_ptr.h" | 14 #include "base/time.h" |
| 14 #include "base/stringprintf.h" | |
| 15 #include "media/base/sinc_resampler.h" | 15 #include "media/base/sinc_resampler.h" |
| 16 #include "testing/gmock/include/gmock/gmock.h" | 16 #include "testing/gmock/include/gmock/gmock.h" |
| 17 #include "testing/gtest/include/gtest/gtest.h" | 17 #include "testing/gtest/include/gtest/gtest.h" |
| 18 | 18 |
| 19 using testing::_; | 19 using testing::_; |
| 20 | 20 |
| 21 namespace media { | 21 namespace media { |
| 22 | 22 |
| 23 static const double kSampleRateRatio = 192000.0 / 44100.0; | |
| 24 static const double kKernelInterpolationFactor = 0.5; | |
| 25 | |
| 23 // Helper class to ensure ChunkedResample() functions properly. | 26 // Helper class to ensure ChunkedResample() functions properly. |
| 24 class MockSource { | 27 class MockSource { |
| 25 public: | 28 public: |
| 26 MOCK_METHOD2(ProvideInput, void(float* destination, int frames)); | 29 MOCK_METHOD2(ProvideInput, void(float* destination, int frames)); |
| 27 }; | 30 }; |
| 28 | 31 |
| 29 // Test requesting multiples of ChunkSize() frames results in the proper number | 32 // Test requesting multiples of ChunkSize() frames results in the proper number |
| 30 // of callbacks. | 33 // of callbacks. |
| 31 TEST(SincResamplerTest, ChunkedResample) { | 34 TEST(SincResamplerTest, ChunkedResample) { |
| 32 MockSource mock_source; | 35 MockSource mock_source; |
| 33 | 36 |
| 34 // Choose a high ratio of input to output samples which will result in quick | 37 // Choose a high ratio of input to output samples which will result in quick |
| 35 // exhaustion of SincResampler's internal buffers. | 38 // exhaustion of SincResampler's internal buffers. |
| 36 static const double kSampleRateRatio = 192000.0 / 44100.0; | |
| 37 SincResampler resampler( | 39 SincResampler resampler( |
| 38 kSampleRateRatio, | 40 kSampleRateRatio, |
| 39 base::Bind(&MockSource::ProvideInput, base::Unretained(&mock_source))); | 41 base::Bind(&MockSource::ProvideInput, base::Unretained(&mock_source))); |
| 40 | 42 |
| 41 static const int kChunks = 2; | 43 static const int kChunks = 2; |
| 42 int max_chunk_size = resampler.ChunkSize() * kChunks; | 44 int max_chunk_size = resampler.ChunkSize() * kChunks; |
| 43 scoped_array<float> resampled_destination(new float[max_chunk_size]); | 45 scoped_array<float> resampled_destination(new float[max_chunk_size]); |
| 44 | 46 |
| 45 // Verify requesting ChunkSize() frames causes a single callback. | 47 // Verify requesting ChunkSize() frames causes a single callback. |
| 46 EXPECT_CALL(mock_source, ProvideInput(_, _)).Times(1); | 48 EXPECT_CALL(mock_source, ProvideInput(_, _)).Times(1); |
| 47 resampler.Resample(resampled_destination.get(), resampler.ChunkSize()); | 49 resampler.Resample(resampled_destination.get(), resampler.ChunkSize()); |
| 48 | 50 |
| 49 // Verify requesting kChunks * ChunkSize() frames causes kChunks callbacks. | 51 // Verify requesting kChunks * ChunkSize() frames causes kChunks callbacks. |
| 50 testing::Mock::VerifyAndClear(&mock_source); | 52 testing::Mock::VerifyAndClear(&mock_source); |
| 51 EXPECT_CALL(mock_source, ProvideInput(_, _)).Times(kChunks); | 53 EXPECT_CALL(mock_source, ProvideInput(_, _)).Times(kChunks); |
| 52 resampler.Resample(resampled_destination.get(), max_chunk_size); | 54 resampler.Resample(resampled_destination.get(), max_chunk_size); |
| 53 } | 55 } |
| 54 | 56 |
| 57 // Ensure various optimized Convolve() methods return the same value. Only run | |
| 58 // this test if other optimized methods exist, otherwise the default Convolve() | |
| 59 // will be tested by the parameterized SincResampler tests below. | |
| 60 #if defined(ARCH_CPU_X86_FAMILY) && defined(__SSE__) | |
| 61 TEST(SincResamplerTest, Convolve) { | |
| 62 // Initialize a dummy resampler. | |
| 63 MockSource mock_source; | |
| 64 SincResampler resampler( | |
| 65 kSampleRateRatio, | |
| 66 base::Bind(&MockSource::ProvideInput, base::Unretained(&mock_source))); | |
| 67 | |
| 68 // Convolve_SSE() is slightly more precise than Conolve_C(), so comparison | |
| 69 // must be done using an epsilon. | |
| 70 static const double kEpsilon = 0.00000005; | |
| 71 | |
| 72 // Use a kernel from SincResampler as input and kernel data, this has the | |
| 73 // benefit of already being properly sized and aligned for Convolve_SSE(). | |
| 74 double result = resampler.Convolve_C( | |
| 75 resampler.kernel_storage_.get(), resampler.kernel_storage_.get(), | |
| 76 resampler.kernel_storage_.get(), kKernelInterpolationFactor); | |
| 77 double result2 = resampler.Convolve_SSE( | |
| 78 resampler.kernel_storage_.get(), resampler.kernel_storage_.get(), | |
| 79 resampler.kernel_storage_.get(), kKernelInterpolationFactor); | |
| 80 EXPECT_NEAR(result2, result, kEpsilon); | |
| 81 | |
| 82 // Test Convolve_SSE() w/ unaligned input pointer. | |
| 83 result = resampler.Convolve_C( | |
| 84 resampler.kernel_storage_.get() + 1, resampler.kernel_storage_.get(), | |
| 85 resampler.kernel_storage_.get(), kKernelInterpolationFactor); | |
| 86 result2 = resampler.Convolve_SSE( | |
| 87 resampler.kernel_storage_.get() + 1, resampler.kernel_storage_.get(), | |
| 88 resampler.kernel_storage_.get(), kKernelInterpolationFactor); | |
| 89 EXPECT_NEAR(result2, result, kEpsilon); | |
| 90 } | |
| 91 #endif | |
| 92 | |
| 93 // Benchmark for the various Convolve() methods. | |
|
Ami GONE FROM CHROMIUM
2012/07/21 17:25:10
Comment that this is only DISABLED_ for the benefi
DaleCurtis
2012/07/24 00:13:07
Done. --convolve-iterations is runtime settable.
| |
| 94 TEST(SincResamplerTest, DISABLED_ConvolveBenchmark) { | |
| 95 // Initialize a dummy resampler. | |
| 96 MockSource mock_source; | |
| 97 SincResampler resampler( | |
| 98 kSampleRateRatio, | |
| 99 base::Bind(&MockSource::ProvideInput, base::Unretained(&mock_source))); | |
| 100 | |
| 101 static const int kConvolveIterations = 50000000; | |
| 102 | |
| 103 // Benchmark Convolve_C(). | |
| 104 base::TimeTicks start = base::TimeTicks::HighResNow(); | |
| 105 for (int i = 0; i < kConvolveIterations; ++i) { | |
| 106 resampler.Convolve_C( | |
| 107 resampler.kernel_storage_.get(), resampler.kernel_storage_.get(), | |
| 108 resampler.kernel_storage_.get(), kKernelInterpolationFactor); | |
| 109 } | |
| 110 base::TimeTicks end = base::TimeTicks::HighResNow(); | |
| 111 printf("Convolve_C took %" PRId64 "ms for %d iterations.\n", | |
| 112 (end - start).InMilliseconds(), kConvolveIterations); | |
| 113 | |
| 114 #if defined(ARCH_CPU_X86_FAMILY) && defined(__SSE__) | |
| 115 // Benchmark Convolve_SSE() with aligned input pointer. | |
| 116 start = base::TimeTicks::HighResNow(); | |
| 117 for (int j = 0; j < kConvolveIterations; ++j) { | |
| 118 resampler.Convolve_SSE( | |
| 119 resampler.kernel_storage_.get(), resampler.kernel_storage_.get(), | |
| 120 resampler.kernel_storage_.get(), kKernelInterpolationFactor); | |
| 121 } | |
| 122 end = base::TimeTicks::HighResNow(); | |
| 123 printf("Convolve_SSE (aligned) took %" PRId64 "ms for %d iterations.\n", | |
| 124 (end - start).InMilliseconds(), kConvolveIterations); | |
| 125 | |
| 126 // Benchmark Convolve_SSE() with unaligned input pointer. | |
| 127 start = base::TimeTicks::HighResNow(); | |
| 128 for (int j = 0; j < kConvolveIterations; ++j) { | |
| 129 resampler.Convolve_SSE( | |
| 130 resampler.kernel_storage_.get() + 1, resampler.kernel_storage_.get(), | |
| 131 resampler.kernel_storage_.get(), kKernelInterpolationFactor); | |
| 132 } | |
| 133 end = base::TimeTicks::HighResNow(); | |
| 134 printf("Convolve_SSE (unaligned) took %" PRId64 "ms for %d iterations.\n", | |
| 135 (end - start).InMilliseconds(), kConvolveIterations); | |
| 136 #endif | |
|
Ami GONE FROM CHROMIUM
2012/07/21 17:25:10
Add an output for the relative improvement between
DaleCurtis
2012/07/24 00:13:07
Done.
| |
| 137 } | |
| 138 | |
| 55 // Fake audio source for testing the resampler. Generates a sinusoidal linear | 139 // Fake audio source for testing the resampler. Generates a sinusoidal linear |
| 56 // chirp (http://en.wikipedia.org/wiki/Chirp) which can be tuned to stress the | 140 // chirp (http://en.wikipedia.org/wiki/Chirp) which can be tuned to stress the |
| 57 // resampler for the specific sample rate conversion being used. | 141 // resampler for the specific sample rate conversion being used. |
| 58 class SinusoidalLinearChirpSource { | 142 class SinusoidalLinearChirpSource { |
| 59 public: | 143 public: |
| 60 SinusoidalLinearChirpSource(int sample_rate, int samples, | 144 SinusoidalLinearChirpSource(int sample_rate, int samples, |
| 61 double max_frequency) | 145 double max_frequency) |
| 62 : sample_rate_(sample_rate), | 146 : sample_rate_(sample_rate), |
| 63 total_samples_(samples), | 147 total_samples_(samples), |
| 64 max_frequency_(max_frequency), | 148 max_frequency_(max_frequency), |
| (...skipping 179 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 244 std::tr1::make_tuple(11025, 192000, kResamplingRMSError, -62.61), | 328 std::tr1::make_tuple(11025, 192000, kResamplingRMSError, -62.61), |
| 245 std::tr1::make_tuple(16000, 192000, kResamplingRMSError, -63.14), | 329 std::tr1::make_tuple(16000, 192000, kResamplingRMSError, -63.14), |
| 246 std::tr1::make_tuple(22050, 192000, kResamplingRMSError, -62.42), | 330 std::tr1::make_tuple(22050, 192000, kResamplingRMSError, -62.42), |
| 247 std::tr1::make_tuple(32000, 192000, kResamplingRMSError, -63.38), | 331 std::tr1::make_tuple(32000, 192000, kResamplingRMSError, -63.38), |
| 248 std::tr1::make_tuple(44100, 192000, kResamplingRMSError, -62.63), | 332 std::tr1::make_tuple(44100, 192000, kResamplingRMSError, -62.63), |
| 249 std::tr1::make_tuple(48000, 192000, kResamplingRMSError, -73.44), | 333 std::tr1::make_tuple(48000, 192000, kResamplingRMSError, -73.44), |
| 250 std::tr1::make_tuple(96000, 192000, kResamplingRMSError, -73.52), | 334 std::tr1::make_tuple(96000, 192000, kResamplingRMSError, -73.52), |
| 251 std::tr1::make_tuple(192000, 192000, kResamplingRMSError, -73.52))); | 335 std::tr1::make_tuple(192000, 192000, kResamplingRMSError, -73.52))); |
| 252 | 336 |
| 253 } // namespace media | 337 } // namespace media |
| OLD | NEW |