Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(91)

Side by Side Diff: media/base/sinc_resampler_unittest.cc

Issue 10702050: Add SincResampler ported from WebKit. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fixes. Created 8 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « media/base/sinc_resampler.cc ('k') | media/media.gyp » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4 // MSVC++ requires this to be set before any other includes to get M_PI.
5 #define _USE_MATH_DEFINES
6
7 #include <cmath>
8
9 #include "base/bind.h"
10 #include "base/bind_helpers.h"
11 #include "base/logging.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "base/stringprintf.h"
14 #include "media/base/sinc_resampler.h"
15 #include "testing/gmock/include/gmock/gmock.h"
16 #include "testing/gtest/include/gtest/gtest.h"
17
18 using testing::_;
19
20 namespace media {
21
22 // Helper class to ensure ChunkedResample() functions properly.
23 class MockSource {
24 public:
25 MOCK_METHOD2(ProvideInput, void(float* destination, int frames));
26 };
27
28 // Test requesting multiples of ChunkSize() frames results in the proper number
29 // of callbacks.
30 TEST(SincResamplerTest, ChunkedResample) {
31 MockSource mock_source;
32
33 // Choose a high ratio of input to output samples which will result in quick
34 // exhaustion of SincResampler's internal buffers.
35 static const double kSampleRateRatio = 192000.0 / 44100.0;
36 SincResampler resampler(
37 kSampleRateRatio,
38 base::Bind(&MockSource::ProvideInput, base::Unretained(&mock_source)));
39
40 static const int kChunks = 2;
41 int max_chunk_size = resampler.ChunkSize() * kChunks;
42 scoped_array<float> resampled_destination(new float[max_chunk_size]);
43
44 // Verify requesting ChunkSize() frames causes a single callback.
45 EXPECT_CALL(mock_source, ProvideInput(_, _)).Times(1);
46 resampler.Resample(resampled_destination.get(), resampler.ChunkSize());
47
48 // Verify requesting kChunks * ChunkSize() frames causes kChunks callbacks.
49 testing::Mock::VerifyAndClear(&mock_source);
50 EXPECT_CALL(mock_source, ProvideInput(_, _)).Times(kChunks);
51 resampler.Resample(resampled_destination.get(), max_chunk_size);
52 }
53
54 // Fake audio source for testing the resampler. Generates a sinusoidal linear
55 // chirp (http://en.wikipedia.org/wiki/Chirp) which can be tuned to stress the
56 // resampler for the specific sample rate conversion being used.
57 class SinusoidalLinearChirpSource {
58 public:
59 SinusoidalLinearChirpSource(int sample_rate, int samples,
60 double max_frequency)
61 : sample_rate_(sample_rate),
62 total_samples_(samples),
63 max_frequency_(max_frequency),
64 current_index_(0) {
65 // Chirp rate.
66 double duration = static_cast<double>(total_samples_) / sample_rate_;
67 k_ = (max_frequency_ - kMinFrequency) / duration;
68 }
69
70 virtual ~SinusoidalLinearChirpSource() {}
71
72 void ProvideInput(float* destination, int frames) {
73 for (int i = 0; i < frames; ++i, ++current_index_) {
74 // Filter out frequencies higher than Nyquist.
75 if (Frequency(current_index_) > 0.5 * sample_rate_) {
76 destination[i] = 0;
77 } else {
78 // Calculate time in seconds.
79 double t = static_cast<double>(current_index_) / sample_rate_;
80
81 // Sinusoidal linear chirp.
82 destination[i] = sin(2 * M_PI * (kMinFrequency * t + (k_ / 2) * t * t));
83 }
84 }
85 }
86
87 double Frequency(int position) {
88 return kMinFrequency + position * (max_frequency_ - kMinFrequency)
89 / total_samples_;
90 }
91
92 private:
93 enum {
94 kMinFrequency = 5
95 };
96
97 double sample_rate_;
98 int total_samples_;
99 double max_frequency_;
100 double k_;
101 int current_index_;
102
103 DISALLOW_COPY_AND_ASSIGN(SinusoidalLinearChirpSource);
104 };
105
106 typedef std::tr1::tuple<int, int, double, double> SincResamplerTestData;
107 class SincResamplerTestCase
108 : public testing::TestWithParam<SincResamplerTestData> {
109 public:
110 SincResamplerTestCase()
111 : input_rate_(std::tr1::get<0>(GetParam())),
112 output_rate_(std::tr1::get<1>(GetParam())),
113 rms_error_(std::tr1::get<2>(GetParam())),
114 low_freq_error_(std::tr1::get<3>(GetParam())) {
115 }
116
117 virtual ~SincResamplerTestCase() {}
118
119 protected:
120 int input_rate_;
121 int output_rate_;
122 double rms_error_;
123 double low_freq_error_;
124 };
125
126 // Tests resampling using a given input and output sample rate.
127 TEST_P(SincResamplerTestCase, Resample) {
128 // Make comparisons using one second of data.
129 static const double kTestDurationSecs = 1;
130 int input_samples = kTestDurationSecs * input_rate_;
131 int output_samples = kTestDurationSecs * output_rate_;
132
133 // Nyquist frequency for the input sampling rate.
134 double input_nyquist_freq = 0.5 * input_rate_;
135
136 // Source for data to be resampled.
137 SinusoidalLinearChirpSource resampler_source(
138 input_rate_, input_samples, input_nyquist_freq);
139
140 SincResampler resampler(
141 input_rate_ / static_cast<double>(output_rate_),
142 base::Bind(&SinusoidalLinearChirpSource::ProvideInput,
143 base::Unretained(&resampler_source)));
144
145 // TODO(dalecurtis): If we switch to AVX/SSE optimization, we'll need to
146 // allocate these on 32-byte boundaries and ensure they're sized % 32 bytes.
147 scoped_array<float> resampled_destination(new float[output_samples]);
148 scoped_array<float> pure_destination(new float[output_samples]);
149
150 // Generate resampled signal.
151 resampler.Resample(resampled_destination.get(), output_samples);
152
153 // Generate pure signal.
154 SinusoidalLinearChirpSource pure_source(
155 output_rate_, output_samples, input_nyquist_freq);
156 pure_source.ProvideInput(pure_destination.get(), output_samples);
157
158 // Range of the Nyquist frequency (0.5 * min(input rate, output_rate)) which
159 // we refer to as low and high.
160 static const double kLowFrequencyNyquistRange = 0.7;
161 static const double kHighFrequencyNyquistRange = 0.9;
162
163 // Calculate Root-Mean-Square-Error and maximum error for the resampling.
164 double sum_of_squares = 0;
165 double low_freq_max_error = 0;
166 double high_freq_max_error = 0;
167 for (int i = 0; i < output_samples; ++i) {
168 double error = fabs(resampled_destination[i] - pure_destination[i]);
169
170 if (pure_source.Frequency(i) < kLowFrequencyNyquistRange * 0.5
171 * std::min(input_rate_, output_rate_)) {
172 if (error > low_freq_max_error)
173 low_freq_max_error = error;
174 } else if (pure_source.Frequency(i) < kHighFrequencyNyquistRange * 0.5
175 * std::min(input_rate_, output_rate_)) {
Chris Rogers 2012/07/11 00:16:49 Instead of repeating std::min(input_rate_, output_
DaleCurtis 2012/07/11 17:17:22 Done.
176 if (error > high_freq_max_error)
177 high_freq_max_error = error;
178 }
179 // TODO(dalecurtis): Sanity check frequencies > kHighFrequencyNyquistRange.
180
181 sum_of_squares += error * error;
182 }
183
184 double rms_error = sqrt(sum_of_squares / output_samples);
185
186 // Convert each error to dbFS.
187 #define DBFS(x) 20 * log10(x)
188 rms_error = DBFS(rms_error);
189 low_freq_max_error = DBFS(low_freq_max_error);
190 high_freq_max_error = DBFS(high_freq_max_error);
191
192 EXPECT_LE(rms_error, rms_error_);
193 EXPECT_LE(low_freq_max_error, low_freq_error_);
194
195 // All conversions currently have a high frequency error around -6 dbFS.
196 static const double kHighFrequencyMaxError = -6.02435;
197 EXPECT_LE(high_freq_max_error, kHighFrequencyMaxError);
198 }
199
200 // Almost all conversions have an RMS error of arond -14 dbFS.
201 static const double kResamplingRMSError = -14.5802;
202
203 // Thresholds chosen arbitrarily based on what each resampling reported during
204 // testing. All thresholds are in dbFS, http://en.wikipedia.org/wiki/DBFS.
205 INSTANTIATE_TEST_CASE_P(
206 SincResamplerTest, SincResamplerTestCase, testing::Values(
207 // To 44.1kHz
208 std::tr1::make_tuple(8000, 44100, kResamplingRMSError, -62.7323),
209 std::tr1::make_tuple(11025, 44100, kResamplingRMSError, -72.1958),
210 std::tr1::make_tuple(16000, 44100, kResamplingRMSError, -62.5423),
211 std::tr1::make_tuple(22050, 44100, kResamplingRMSError, -73.5389),
212 std::tr1::make_tuple(32000, 44100, kResamplingRMSError, -63.3289),
213 std::tr1::make_tuple(44100, 44100, kResamplingRMSError, -73.5364),
214 std::tr1::make_tuple(48000, 44100, -15.0114, -64.0425),
215 std::tr1::make_tuple(96000, 44100, -18.4991, -25.5177),
216 std::tr1::make_tuple(192000, 44100, -20.5068, -13.3184),
217
218 // To 48kHz
219 std::tr1::make_tuple(8000, 48000, kResamplingRMSError, -63.4359),
220 std::tr1::make_tuple(11025, 48000, kResamplingRMSError, -62.6140),
221 std::tr1::make_tuple(16000, 48000, kResamplingRMSError, -63.9629),
222 std::tr1::make_tuple(22050, 48000, kResamplingRMSError, -62.4224),
223 std::tr1::make_tuple(32000, 48000, kResamplingRMSError, -64.0417),
224 std::tr1::make_tuple(44100, 48000, kResamplingRMSError, -62.6364),
225 std::tr1::make_tuple(48000, 48000, kResamplingRMSError, -73.5241),
226 std::tr1::make_tuple(96000, 48000, -18.403, -28.4466),
227 std::tr1::make_tuple(192000, 48000, -20.4382, -14.1110),
228
229 // To 96kHz
230 std::tr1::make_tuple(8000, 96000, kResamplingRMSError, -63.1999),
231 std::tr1::make_tuple(11025, 96000, kResamplingRMSError, -62.6140),
232 std::tr1::make_tuple(16000, 96000, kResamplingRMSError, -63.3983),
233 std::tr1::make_tuple(22050, 96000, kResamplingRMSError, -62.4224),
234 std::tr1::make_tuple(32000, 96000, kResamplingRMSError, -63.9571),
235 std::tr1::make_tuple(44100, 96000, kResamplingRMSError, -62.6364),
236 std::tr1::make_tuple(48000, 96000, kResamplingRMSError, -73.5241),
237 std::tr1::make_tuple(96000, 96000, kResamplingRMSError, -73.5266),
238 std::tr1::make_tuple(192000, 96000, kResamplingRMSError, -28.4128),
239
240 // To 192kHz
241 std::tr1::make_tuple(8000, 192000, kResamplingRMSError, -63.1017),
242 std::tr1::make_tuple(11025, 192000, kResamplingRMSError, -62.6140),
243 std::tr1::make_tuple(16000, 192000, kResamplingRMSError, -63.1433),
244 std::tr1::make_tuple(22050, 192000, kResamplingRMSError, -62.4231),
245 std::tr1::make_tuple(32000, 192000, kResamplingRMSError, -63.3822),
246 std::tr1::make_tuple(44100, 192000, kResamplingRMSError, -62.6364),
247 std::tr1::make_tuple(48000, 192000, kResamplingRMSError, -73.4483),
248 std::tr1::make_tuple(96000, 192000, kResamplingRMSError, -73.5266),
249 std::tr1::make_tuple(192000, 192000, kResamplingRMSError, -73.5266)));
250
251 } // namespace media
OLDNEW
« no previous file with comments | « media/base/sinc_resampler.cc ('k') | media/media.gyp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698