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/command_line.h" |
12 #include "base/logging.h" | 13 #include "base/logging.h" |
13 #include "base/memory/scoped_ptr.h" | 14 #include "base/string_number_conversions.h" |
14 #include "base/stringprintf.h" | 15 #include "base/time.h" |
15 #include "media/base/sinc_resampler.h" | 16 #include "media/base/sinc_resampler.h" |
16 #include "testing/gmock/include/gmock/gmock.h" | 17 #include "testing/gmock/include/gmock/gmock.h" |
17 #include "testing/gtest/include/gtest/gtest.h" | 18 #include "testing/gtest/include/gtest/gtest.h" |
18 | 19 |
19 using testing::_; | 20 using testing::_; |
20 | 21 |
21 namespace media { | 22 namespace media { |
22 | 23 |
| 24 static const double kSampleRateRatio = 192000.0 / 44100.0; |
| 25 static const double kKernelInterpolationFactor = 0.5; |
| 26 |
| 27 // Command line switch for runtime adjustment of ConvolveBenchmark iterations. |
| 28 static const char kConvolveIterations[] = "convolve-iterations"; |
| 29 |
23 // Helper class to ensure ChunkedResample() functions properly. | 30 // Helper class to ensure ChunkedResample() functions properly. |
24 class MockSource { | 31 class MockSource { |
25 public: | 32 public: |
26 MOCK_METHOD2(ProvideInput, void(float* destination, int frames)); | 33 MOCK_METHOD2(ProvideInput, void(float* destination, int frames)); |
27 }; | 34 }; |
28 | 35 |
29 // Test requesting multiples of ChunkSize() frames results in the proper number | 36 // Test requesting multiples of ChunkSize() frames results in the proper number |
30 // of callbacks. | 37 // of callbacks. |
31 TEST(SincResamplerTest, ChunkedResample) { | 38 TEST(SincResamplerTest, ChunkedResample) { |
32 MockSource mock_source; | 39 MockSource mock_source; |
33 | 40 |
34 // Choose a high ratio of input to output samples which will result in quick | 41 // Choose a high ratio of input to output samples which will result in quick |
35 // exhaustion of SincResampler's internal buffers. | 42 // exhaustion of SincResampler's internal buffers. |
36 static const double kSampleRateRatio = 192000.0 / 44100.0; | |
37 SincResampler resampler( | 43 SincResampler resampler( |
38 kSampleRateRatio, | 44 kSampleRateRatio, |
39 base::Bind(&MockSource::ProvideInput, base::Unretained(&mock_source))); | 45 base::Bind(&MockSource::ProvideInput, base::Unretained(&mock_source))); |
40 | 46 |
41 static const int kChunks = 2; | 47 static const int kChunks = 2; |
42 int max_chunk_size = resampler.ChunkSize() * kChunks; | 48 int max_chunk_size = resampler.ChunkSize() * kChunks; |
43 scoped_array<float> resampled_destination(new float[max_chunk_size]); | 49 scoped_array<float> resampled_destination(new float[max_chunk_size]); |
44 | 50 |
45 // Verify requesting ChunkSize() frames causes a single callback. | 51 // Verify requesting ChunkSize() frames causes a single callback. |
46 EXPECT_CALL(mock_source, ProvideInput(_, _)).Times(1); | 52 EXPECT_CALL(mock_source, ProvideInput(_, _)).Times(1); |
47 resampler.Resample(resampled_destination.get(), resampler.ChunkSize()); | 53 resampler.Resample(resampled_destination.get(), resampler.ChunkSize()); |
48 | 54 |
49 // Verify requesting kChunks * ChunkSize() frames causes kChunks callbacks. | 55 // Verify requesting kChunks * ChunkSize() frames causes kChunks callbacks. |
50 testing::Mock::VerifyAndClear(&mock_source); | 56 testing::Mock::VerifyAndClear(&mock_source); |
51 EXPECT_CALL(mock_source, ProvideInput(_, _)).Times(kChunks); | 57 EXPECT_CALL(mock_source, ProvideInput(_, _)).Times(kChunks); |
52 resampler.Resample(resampled_destination.get(), max_chunk_size); | 58 resampler.Resample(resampled_destination.get(), max_chunk_size); |
53 } | 59 } |
54 | 60 |
| 61 // Ensure various optimized Convolve() methods return the same value. Only run |
| 62 // this test if other optimized methods exist, otherwise the default Convolve() |
| 63 // will be tested by the parameterized SincResampler tests below. |
| 64 #if defined(ARCH_CPU_X86_FAMILY) && defined(__SSE__) |
| 65 TEST(SincResamplerTest, Convolve) { |
| 66 // Initialize a dummy resampler. |
| 67 MockSource mock_source; |
| 68 SincResampler resampler( |
| 69 kSampleRateRatio, |
| 70 base::Bind(&MockSource::ProvideInput, base::Unretained(&mock_source))); |
| 71 |
| 72 // Convolve_SSE() is slightly more precise than Convolve_C(), so comparison |
| 73 // must be done using an epsilon. |
| 74 static const double kEpsilon = 0.00000005; |
| 75 |
| 76 // Use a kernel from SincResampler as input and kernel data, this has the |
| 77 // benefit of already being properly sized and aligned for Convolve_SSE(). |
| 78 double result = resampler.Convolve_C( |
| 79 resampler.kernel_storage_.get(), resampler.kernel_storage_.get(), |
| 80 resampler.kernel_storage_.get(), kKernelInterpolationFactor); |
| 81 double result2 = resampler.Convolve_SSE( |
| 82 resampler.kernel_storage_.get(), resampler.kernel_storage_.get(), |
| 83 resampler.kernel_storage_.get(), kKernelInterpolationFactor); |
| 84 EXPECT_NEAR(result2, result, kEpsilon); |
| 85 |
| 86 // Test Convolve_SSE() w/ unaligned input pointer. |
| 87 result = resampler.Convolve_C( |
| 88 resampler.kernel_storage_.get() + 1, resampler.kernel_storage_.get(), |
| 89 resampler.kernel_storage_.get(), kKernelInterpolationFactor); |
| 90 result2 = resampler.Convolve_SSE( |
| 91 resampler.kernel_storage_.get() + 1, resampler.kernel_storage_.get(), |
| 92 resampler.kernel_storage_.get(), kKernelInterpolationFactor); |
| 93 EXPECT_NEAR(result2, result, kEpsilon); |
| 94 } |
| 95 #endif |
| 96 |
| 97 // Benchmark for the various Convolve() methods. Make sure to build with |
| 98 // branding=Chrome so that DCHECKs are compiled out when benchmarking. Original |
| 99 // benchmarks were run with --convolve-iterations=50000000. |
| 100 TEST(SincResamplerTest, ConvolveBenchmark) { |
| 101 // Initialize a dummy resampler. |
| 102 MockSource mock_source; |
| 103 SincResampler resampler( |
| 104 kSampleRateRatio, |
| 105 base::Bind(&MockSource::ProvideInput, base::Unretained(&mock_source))); |
| 106 |
| 107 // Retrieve benchmark iterations from command line. |
| 108 int convolve_iterations = 10; |
| 109 std::string iterations(CommandLine::ForCurrentProcess()->GetSwitchValueASCII( |
| 110 kConvolveIterations)); |
| 111 if (!iterations.empty()) |
| 112 base::StringToInt(iterations, &convolve_iterations); |
| 113 |
| 114 printf("Benchmarking %d iterations:\n", convolve_iterations); |
| 115 |
| 116 // Benchmark Convolve_C(). |
| 117 base::TimeTicks start = base::TimeTicks::HighResNow(); |
| 118 for (int i = 0; i < convolve_iterations; ++i) { |
| 119 resampler.Convolve_C( |
| 120 resampler.kernel_storage_.get(), resampler.kernel_storage_.get(), |
| 121 resampler.kernel_storage_.get(), kKernelInterpolationFactor); |
| 122 } |
| 123 double total_time_c_ms = |
| 124 (base::TimeTicks::HighResNow() - start).InMillisecondsF(); |
| 125 printf("Convolve_C took %.2fms.\n", total_time_c_ms); |
| 126 |
| 127 #if defined(ARCH_CPU_X86_FAMILY) && defined(__SSE__) |
| 128 // Benchmark Convolve_SSE() with unaligned input pointer. |
| 129 start = base::TimeTicks::HighResNow(); |
| 130 for (int j = 0; j < convolve_iterations; ++j) { |
| 131 resampler.Convolve_SSE( |
| 132 resampler.kernel_storage_.get() + 1, resampler.kernel_storage_.get(), |
| 133 resampler.kernel_storage_.get(), kKernelInterpolationFactor); |
| 134 } |
| 135 double total_time_sse_unaligned_ms = |
| 136 (base::TimeTicks::HighResNow() - start).InMillisecondsF(); |
| 137 printf("Convolve_SSE (unaligned) took %.2fms; which is %.2fx faster than" |
| 138 " Convolve_C.\n", total_time_sse_unaligned_ms, |
| 139 total_time_c_ms / total_time_sse_unaligned_ms); |
| 140 |
| 141 // Benchmark Convolve_SSE() with aligned input pointer. |
| 142 start = base::TimeTicks::HighResNow(); |
| 143 for (int j = 0; j < convolve_iterations; ++j) { |
| 144 resampler.Convolve_SSE( |
| 145 resampler.kernel_storage_.get(), resampler.kernel_storage_.get(), |
| 146 resampler.kernel_storage_.get(), kKernelInterpolationFactor); |
| 147 } |
| 148 double total_time_sse_aligned_ms = |
| 149 (base::TimeTicks::HighResNow() - start).InMillisecondsF(); |
| 150 printf("Convolve_SSE (aligned) took %.2fms; which is %.2fx faster than" |
| 151 " Convolve_C and %.2fx faster than Convolve_SSE (unaligned).\n", |
| 152 total_time_sse_aligned_ms, total_time_c_ms / total_time_sse_aligned_ms, |
| 153 total_time_sse_unaligned_ms / total_time_sse_aligned_ms); |
| 154 #endif |
| 155 } |
| 156 |
55 // Fake audio source for testing the resampler. Generates a sinusoidal linear | 157 // 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 | 158 // chirp (http://en.wikipedia.org/wiki/Chirp) which can be tuned to stress the |
57 // resampler for the specific sample rate conversion being used. | 159 // resampler for the specific sample rate conversion being used. |
58 class SinusoidalLinearChirpSource { | 160 class SinusoidalLinearChirpSource { |
59 public: | 161 public: |
60 SinusoidalLinearChirpSource(int sample_rate, int samples, | 162 SinusoidalLinearChirpSource(int sample_rate, int samples, |
61 double max_frequency) | 163 double max_frequency) |
62 : sample_rate_(sample_rate), | 164 : sample_rate_(sample_rate), |
63 total_samples_(samples), | 165 total_samples_(samples), |
64 max_frequency_(max_frequency), | 166 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), | 346 std::tr1::make_tuple(11025, 192000, kResamplingRMSError, -62.61), |
245 std::tr1::make_tuple(16000, 192000, kResamplingRMSError, -63.14), | 347 std::tr1::make_tuple(16000, 192000, kResamplingRMSError, -63.14), |
246 std::tr1::make_tuple(22050, 192000, kResamplingRMSError, -62.42), | 348 std::tr1::make_tuple(22050, 192000, kResamplingRMSError, -62.42), |
247 std::tr1::make_tuple(32000, 192000, kResamplingRMSError, -63.38), | 349 std::tr1::make_tuple(32000, 192000, kResamplingRMSError, -63.38), |
248 std::tr1::make_tuple(44100, 192000, kResamplingRMSError, -62.63), | 350 std::tr1::make_tuple(44100, 192000, kResamplingRMSError, -62.63), |
249 std::tr1::make_tuple(48000, 192000, kResamplingRMSError, -73.44), | 351 std::tr1::make_tuple(48000, 192000, kResamplingRMSError, -73.44), |
250 std::tr1::make_tuple(96000, 192000, kResamplingRMSError, -73.52), | 352 std::tr1::make_tuple(96000, 192000, kResamplingRMSError, -73.52), |
251 std::tr1::make_tuple(192000, 192000, kResamplingRMSError, -73.52))); | 353 std::tr1::make_tuple(192000, 192000, kResamplingRMSError, -73.52))); |
252 | 354 |
253 } // namespace media | 355 } // namespace media |
OLD | NEW |