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

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: Remove unused variable. 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
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 namespace media {
19
20 // Range of the Nyquist frequency (0.5 * min(input rate, output_rate)) which
Ami GONE FROM CHROMIUM 2012/07/10 03:23:00 All these consts could move south to above where t
DaleCurtis 2012/07/10 21:38:55 Done.
21 // we refer to as low and high.
22 static const double kLowFrequencyNyquistRange = 0.7;
23 static const double kHighFrequencyNyquistRange = 0.9;
24
25 // All conversions currently have a high frequency error of ~0.5.
26 static const double kHighFrequencyMaxError = 0.5;
27
28 // Almost all conversions have an RMS error of ~0.19.
29 static const double kRMSError = 0.19;
Ami GONE FROM CHROMIUM 2012/07/10 03:23:00 This is a terrible variable name ;)
DaleCurtis 2012/07/10 21:38:55 Done.
30
31 // Helper class to ensure ChunkedResample() functions properly.
32 class MockSource {
33 public:
34 MOCK_METHOD2(ProvideInput, void(float* destination, int frames));
35 };
36
37 // Fake audio source for testing the resampler. Generates a sinusoidal linear
38 // chirp (http://en.wikipedia.org/wiki/Chirp) which can be tuned to stress the
39 // resampler for the specific sample rate conversion being used.
40 class SinusoidalLinearChirpSource {
41 public:
42 SinusoidalLinearChirpSource(int sample_rate, int samples,
43 double max_frequency)
44 : sample_rate_(sample_rate),
45 total_samples_(samples),
46 max_frequency_(max_frequency),
47 current_index_(0) {
48 // Chirp rate.
49 double duration = static_cast<double>(total_samples_) / sample_rate_;
50 k_ = (max_frequency_ - kMinFrequency) / (2 * duration);
Chris Rogers 2012/07/10 17:37:11 I see you've tried to optimize the 0.5 factor into
DaleCurtis 2012/07/10 21:38:55 Done.
51 }
52
53 virtual ~SinusoidalLinearChirpSource() {}
54
55 void ProvideInput(float* destination, int frames) {
56 for (int i = 0; i < frames; ++i, ++current_index_) {
57 // Filter out frequencies higher than Nyquist.
58 if (Frequency(current_index_) > 0.5 * sample_rate_) {
59 destination[i] = 0;
60 } else {
61 // Calculate time in seconds.
62 double t = static_cast<double>(current_index_) / sample_rate_;
63
64 // Sinusoidal linear chirp.
65 destination[i] = sin(2 * M_PI * (kMinFrequency * t + k_ * t * t));
66 }
67 }
68 }
69
70 double Frequency(int position) {
71 return kMinFrequency + position * (max_frequency_ - kMinFrequency)
72 / total_samples_;
73 }
74
75 private:
76 enum {
77 kMinFrequency = 5
78 };
79
80 double sample_rate_;
81 int total_samples_;
82 double max_frequency_;
83 double k_;
84 int current_index_;
85
86 DISALLOW_COPY_AND_ASSIGN(SinusoidalLinearChirpSource);
87 };
88
89 typedef std::tr1::tuple<int, int, double, double> SincResamplerTestData;
90 class SincResamplerTestCase
91 : public testing::TestWithParam<SincResamplerTestData> {
92 public:
93 SincResamplerTestCase()
94 : input_rate_(std::tr1::get<0>(GetParam())),
95 output_rate_(std::tr1::get<1>(GetParam())),
96 rms_error_(std::tr1::get<2>(GetParam())),
97 low_freq_error_(std::tr1::get<3>(GetParam())) {
98 }
99
100 virtual ~SincResamplerTestCase() {}
101
102 protected:
103 int input_rate_;
104 int output_rate_;
105 double rms_error_;
106 double low_freq_error_;
107 };
108
109 // Test requesting ChunkSize() frames only results in a single callback call.
Ami GONE FROM CHROMIUM 2012/07/10 03:23:00 Do you want to also assert that ChunkSize()+1 resu
DaleCurtis 2012/07/10 21:38:55 Done.
110 TEST(SincResamplerTest, ChunkedResample) {
Ami GONE FROM CHROMIUM 2012/07/10 03:23:00 I'd put this above SRTC to clarify it's got nothin
DaleCurtis 2012/07/10 21:38:55 Done.
111 MockSource mock_source;
112 EXPECT_CALL(mock_source, ProvideInput(testing::_, testing::_)).Times(1);
113
114 // Choose a high ratio of input to output samples which will result in quick
115 // exhaustion of SincResampler's internal buffers.
116 static const double kSampleRateRatio = 192000.0f / 48000.0f;
Ami GONE FROM CHROMIUM 2012/07/10 03:23:00 It's strange that the RHS uses 'f's though the res
DaleCurtis 2012/07/10 21:38:55 Done.
117 SincResampler resampler(
118 kSampleRateRatio,
119 base::Bind(&MockSource::ProvideInput, base::Unretained(&mock_source)));
120
121 scoped_array<float> resampled_destination(new float[resampler.ChunkSize()]);
122 resampler.Resample(resampled_destination.get(), resampler.ChunkSize());
123 }
124
125 // Tests resampling using a given input and output sample rate.
126 TEST_P(SincResamplerTestCase, Resample) {
127 // Make comparisons using one second of data.
128 static const double kTestDurationSecs = 1;
129 int input_samples = kTestDurationSecs * input_rate_;
130 int output_samples = kTestDurationSecs * output_rate_;
131
132 // Nyquist frequency for the input sampling rate.
133 double input_nyquist_freq = 0.5 * input_rate_;
134
135 // Source for data to be resampled.
136 SinusoidalLinearChirpSource resampler_source(
137 input_rate_, input_samples, input_nyquist_freq);
138
139 SincResampler resampler(
140 input_rate_ / static_cast<double>(output_rate_),
141 base::Bind(&SinusoidalLinearChirpSource::ProvideInput,
142 base::Unretained(&resampler_source)));
143
144 // TODO(dalecurtis): If we switch to AVX/SSE optimization, we'll need to
145 // allocate these on 32-byte boundaries and ensure they're sized % 32 bytes.
146 scoped_array<float> resampled_destination(new float[output_samples]);
147 scoped_array<float> pure_destination(new float[output_samples]);
148
149 // Generate resampled signal.
150 resampler.Resample(resampled_destination.get(), output_samples);
151
152 // Generate pure signal.
153 SinusoidalLinearChirpSource pure_source(
154 output_rate_, output_samples, input_nyquist_freq);
155 pure_source.ProvideInput(pure_destination.get(), output_samples);
156
157
Chris Rogers 2012/07/10 17:37:11 nit: extra blank line
DaleCurtis 2012/07/10 21:38:55 Done.
158 // Calculate Root-Mean-Square-Error and maximum error for the resampling.
159 double sum_of_squares = 0;
160 double low_freq_max_error = 0;
161 double high_freq_max_error = 0;
162 for (int i = 0; i < output_samples; ++i) {
163 double error = fabs(resampled_destination[i] - pure_destination[i]);
164
165 if (pure_source.Frequency(i) < kLowFrequencyNyquistRange * 0.5
166 * std::min(input_rate_, output_rate_)) {
167 if (error > low_freq_max_error)
168 low_freq_max_error = error;
169 } else if (pure_source.Frequency(i) < kHighFrequencyNyquistRange * 0.5
170 * std::min(input_rate_, output_rate_)) {
171 if (error > high_freq_max_error)
172 high_freq_max_error = error;
173 }
Chris Rogers 2012/07/10 17:37:11 add TODO to sanity-check frequencies > kHighFreque
DaleCurtis 2012/07/10 21:38:55 Done. I'm still not sure I understand what to do t
174
175 sum_of_squares += error * error;
176 }
177
178 double rms_error = sqrt(sum_of_squares / output_samples);
179
180 EXPECT_LT(rms_error, rms_error_);
181 EXPECT_LT(low_freq_max_error, low_freq_error_);
182 EXPECT_LT(high_freq_max_error, kHighFrequencyMaxError);
183 }
184
185 // TODO(dalecurtis): Thresholds should be made tighter once we switch to a
Ami GONE FROM CHROMIUM 2012/07/10 03:23:00 You used to have a comment to the effect that thes
Chris Rogers 2012/07/10 17:37:11 I agree. Just to clarify, the errors we're seeing
Chris Rogers 2012/07/10 17:37:11 Please add TODO that we could test the high freque
DaleCurtis 2012/07/10 21:38:55 Done.
DaleCurtis 2012/07/10 21:38:55 This is covered in the TODO above?
186 // higher kernel size.
187 INSTANTIATE_TEST_CASE_P(
188 SincResamplerTest, SincResamplerTestCase, testing::Values(
189 // To 44.1kHz
190 std::tr1::make_tuple(8000, 44100, kRMSError, 0.00074),
Chris Rogers 2012/07/10 17:37:11 My preference would have been to have these aliasi
DaleCurtis 2012/07/10 21:38:55 Done.
191 std::tr1::make_tuple(11025, 44100, kRMSError, 0.00025),
192 std::tr1::make_tuple(16000, 44100, kRMSError, 0.00075),
193 std::tr1::make_tuple(22050, 44100, kRMSError, 0.00022),
194 std::tr1::make_tuple(32000, 44100, kRMSError, 0.00069),
195 std::tr1::make_tuple(44100, 44100, kRMSError, 0.00022),
196 std::tr1::make_tuple(48000, 44100, 0.18, 0.00063),
197 std::tr1::make_tuple(96000, 44100, 0.12, 0.053),
198 std::tr1::make_tuple(192000, 44100, 0.095, 0.22),
199
200 // To 48kHz
201 std::tr1::make_tuple(8000, 48000, kRMSError, 0.00068),
202 std::tr1::make_tuple(11025, 48000, kRMSError, 0.00075),
203 std::tr1::make_tuple(16000, 48000, kRMSError, 0.00064),
204 std::tr1::make_tuple(22050, 48000, kRMSError, 0.00076),
205 std::tr1::make_tuple(32000, 48000, kRMSError, 0.00063),
206 std::tr1::make_tuple(44100, 48000, kRMSError, 0.00074),
207 std::tr1::make_tuple(48000, 48000, kRMSError, 0.00022),
208 std::tr1::make_tuple(96000, 48000, 0.13, 0.038),
209 std::tr1::make_tuple(192000, 48000, 0.096, 0.20),
210
211 // To 96kHz
212 std::tr1::make_tuple(8000, 96000, kRMSError, 0.00070),
213 std::tr1::make_tuple(11025, 96000, kRMSError, 0.00075),
214 std::tr1::make_tuple(16000, 96000, kRMSError, 0.00068),
215 std::tr1::make_tuple(22050, 96000, kRMSError, 0.00076),
216 std::tr1::make_tuple(32000, 96000, kRMSError, 0.00064),
217 std::tr1::make_tuple(44100, 96000, kRMSError, 0.00074),
218 std::tr1::make_tuple(48000, 96000, kRMSError, 0.00022),
219 std::tr1::make_tuple(96000, 96000, kRMSError, 0.00022),
220 std::tr1::make_tuple(192000, 96000, kRMSError, 0.038),
221
222 // To 192kHz
223 std::tr1::make_tuple(8000, 192000, kRMSError, 0.00070),
224 std::tr1::make_tuple(11025, 192000, kRMSError, 0.00075),
225 std::tr1::make_tuple(16000, 192000, kRMSError, 0.00070),
226 std::tr1::make_tuple(22050, 192000, kRMSError, 0.00076),
227 std::tr1::make_tuple(32000, 192000, kRMSError, 0.00068),
228 std::tr1::make_tuple(44100, 192000, kRMSError, 0.00074),
229 std::tr1::make_tuple(48000, 192000, kRMSError, 0.00022),
230 std::tr1::make_tuple(96000, 192000, kRMSError, 0.00022),
231 std::tr1::make_tuple(192000, 192000, kRMSError, 0.00022)));
232
233 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698