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
5 // MSVC++ requires this to be set before any other includes to get M_PI.
6 #define _USE_MATH_DEFINES
7 #include <cmath>
8
9 #include "base/cpu.h"
10 #include "base/memory/aligned_memory.h"
11 #include "base/memory/scoped_ptr.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/strings/stringize_macros.h"
14 #include "media/base/vector_math.h"
15 #include "media/base/vector_math_testing.h"
16 #include "testing/gtest/include/gtest/gtest.h"
17
18 using std::fill;
19
20 namespace media {
21
22 // Default test values.
23 static const float kScale = 0.5;
24 static const float kInputFillValue = 1.0;
25 static const float kOutputFillValue = 3.0;
26 static const int kVectorSize = 8192;
27
28 class VectorMathTest : public testing::Test {
29 public:
30
VectorMathTest()31 VectorMathTest() {
32 // Initialize input and output vectors.
33 input_vector_.reset(static_cast<float*>(base::AlignedAlloc(
34 sizeof(float) * kVectorSize, vector_math::kRequiredAlignment)));
35 output_vector_.reset(static_cast<float*>(base::AlignedAlloc(
36 sizeof(float) * kVectorSize, vector_math::kRequiredAlignment)));
37 }
38
FillTestVectors(float input,float output)39 void FillTestVectors(float input, float output) {
40 // Setup input and output vectors.
41 fill(input_vector_.get(), input_vector_.get() + kVectorSize, input);
42 fill(output_vector_.get(), output_vector_.get() + kVectorSize, output);
43 }
44
VerifyOutput(float value)45 void VerifyOutput(float value) {
46 for (int i = 0; i < kVectorSize; ++i)
47 ASSERT_FLOAT_EQ(output_vector_.get()[i], value);
48 }
49
50 protected:
51 scoped_ptr_malloc<float, base::ScopedPtrAlignedFree> input_vector_;
52 scoped_ptr_malloc<float, base::ScopedPtrAlignedFree> output_vector_;
53
54 DISALLOW_COPY_AND_ASSIGN(VectorMathTest);
55 };
56
57 // Ensure each optimized vector_math::FMAC() method returns the same value.
TEST_F(VectorMathTest,FMAC)58 TEST_F(VectorMathTest, FMAC) {
59 static const float kResult = kInputFillValue * kScale + kOutputFillValue;
60
61 {
62 SCOPED_TRACE("FMAC");
63 FillTestVectors(kInputFillValue, kOutputFillValue);
64 vector_math::FMAC(
65 input_vector_.get(), kScale, kVectorSize, output_vector_.get());
66 VerifyOutput(kResult);
67 }
68
69 {
70 SCOPED_TRACE("FMAC_C");
71 FillTestVectors(kInputFillValue, kOutputFillValue);
72 vector_math::FMAC_C(
73 input_vector_.get(), kScale, kVectorSize, output_vector_.get());
74 VerifyOutput(kResult);
75 }
76
77 #if defined(ARCH_CPU_X86_FAMILY)
78 {
79 ASSERT_TRUE(base::CPU().has_sse());
80 SCOPED_TRACE("FMAC_SSE");
81 FillTestVectors(kInputFillValue, kOutputFillValue);
82 vector_math::FMAC_SSE(
83 input_vector_.get(), kScale, kVectorSize, output_vector_.get());
84 VerifyOutput(kResult);
85 }
86 #endif
87
88 #if defined(ARCH_CPU_ARM_FAMILY) && defined(USE_NEON)
89 {
90 SCOPED_TRACE("FMAC_NEON");
91 FillTestVectors(kInputFillValue, kOutputFillValue);
92 vector_math::FMAC_NEON(
93 input_vector_.get(), kScale, kVectorSize, output_vector_.get());
94 VerifyOutput(kResult);
95 }
96 #endif
97 }
98
99 // Ensure each optimized vector_math::FMUL() method returns the same value.
TEST_F(VectorMathTest,FMUL)100 TEST_F(VectorMathTest, FMUL) {
101 static const float kResult = kInputFillValue * kScale;
102
103 {
104 SCOPED_TRACE("FMUL");
105 FillTestVectors(kInputFillValue, kOutputFillValue);
106 vector_math::FMUL(
107 input_vector_.get(), kScale, kVectorSize, output_vector_.get());
108 VerifyOutput(kResult);
109 }
110
111 {
112 SCOPED_TRACE("FMUL_C");
113 FillTestVectors(kInputFillValue, kOutputFillValue);
114 vector_math::FMUL_C(
115 input_vector_.get(), kScale, kVectorSize, output_vector_.get());
116 VerifyOutput(kResult);
117 }
118
119 #if defined(ARCH_CPU_X86_FAMILY)
120 {
121 ASSERT_TRUE(base::CPU().has_sse());
122 SCOPED_TRACE("FMUL_SSE");
123 FillTestVectors(kInputFillValue, kOutputFillValue);
124 vector_math::FMUL_SSE(
125 input_vector_.get(), kScale, kVectorSize, output_vector_.get());
126 VerifyOutput(kResult);
127 }
128 #endif
129
130 #if defined(ARCH_CPU_ARM_FAMILY) && defined(USE_NEON)
131 {
132 SCOPED_TRACE("FMUL_NEON");
133 FillTestVectors(kInputFillValue, kOutputFillValue);
134 vector_math::FMUL_NEON(
135 input_vector_.get(), kScale, kVectorSize, output_vector_.get());
136 VerifyOutput(kResult);
137 }
138 #endif
139 }
140
141 namespace {
142
143 class EWMATestScenario {
144 public:
EWMATestScenario(float initial_value,const float src[],int len,float smoothing_factor)145 EWMATestScenario(float initial_value, const float src[], int len,
146 float smoothing_factor)
147 : initial_value_(initial_value),
148 data_(static_cast<float*>(
149 len == 0 ? NULL :
150 base::AlignedAlloc(len * sizeof(float),
151 vector_math::kRequiredAlignment))),
152 data_len_(len),
153 smoothing_factor_(smoothing_factor),
154 expected_final_avg_(initial_value),
155 expected_max_(0.0f) {
156 if (data_len_ > 0)
157 memcpy(data_.get(), src, len * sizeof(float));
158 }
159
160 // Copy constructor and assignment operator for ::testing::Values(...).
EWMATestScenario(const EWMATestScenario & other)161 EWMATestScenario(const EWMATestScenario& other) { *this = other; }
operator =(const EWMATestScenario & other)162 EWMATestScenario& operator=(const EWMATestScenario& other) {
163 this->initial_value_ = other.initial_value_;
164 this->smoothing_factor_ = other.smoothing_factor_;
165 if (other.data_len_ == 0) {
166 this->data_.reset();
167 } else {
168 this->data_.reset(static_cast<float*>(
169 base::AlignedAlloc(other.data_len_ * sizeof(float),
170 vector_math::kRequiredAlignment)));
171 memcpy(this->data_.get(), other.data_.get(),
172 other.data_len_ * sizeof(float));
173 }
174 this->data_len_ = other.data_len_;
175 this->expected_final_avg_ = other.expected_final_avg_;
176 this->expected_max_ = other.expected_max_;
177 return *this;
178 }
179
ScaledBy(float scale) const180 EWMATestScenario ScaledBy(float scale) const {
181 EWMATestScenario result(*this);
182 float* p = result.data_.get();
183 float* const p_end = p + result.data_len_;
184 for (; p < p_end; ++p)
185 *p *= scale;
186 return result;
187 }
188
WithImpulse(float value,int offset) const189 EWMATestScenario WithImpulse(float value, int offset) const {
190 EWMATestScenario result(*this);
191 result.data_.get()[offset] = value;
192 return result;
193 }
194
HasExpectedResult(float final_avg_value,float max_value) const195 EWMATestScenario HasExpectedResult(float final_avg_value,
196 float max_value) const {
197 EWMATestScenario result(*this);
198 result.expected_final_avg_ = final_avg_value;
199 result.expected_max_ = max_value;
200 return result;
201 }
202
RunTest() const203 void RunTest() const {
204 {
205 SCOPED_TRACE("EWMAAndMaxPower");
206 const std::pair<float, float>& result = vector_math::EWMAAndMaxPower(
207 initial_value_, data_.get(), data_len_, smoothing_factor_);
208 EXPECT_NEAR(expected_final_avg_, result.first, 0.0000001f);
209 EXPECT_NEAR(expected_max_, result.second, 0.0000001f);
210 }
211
212 {
213 SCOPED_TRACE("EWMAAndMaxPower_C");
214 const std::pair<float, float>& result = vector_math::EWMAAndMaxPower_C(
215 initial_value_, data_.get(), data_len_, smoothing_factor_);
216 EXPECT_NEAR(expected_final_avg_, result.first, 0.0000001f);
217 EXPECT_NEAR(expected_max_, result.second, 0.0000001f);
218 }
219
220 #if defined(ARCH_CPU_X86_FAMILY)
221 {
222 ASSERT_TRUE(base::CPU().has_sse());
223 SCOPED_TRACE("EWMAAndMaxPower_SSE");
224 const std::pair<float, float>& result = vector_math::EWMAAndMaxPower_SSE(
225 initial_value_, data_.get(), data_len_, smoothing_factor_);
226 EXPECT_NEAR(expected_final_avg_, result.first, 0.0000001f);
227 EXPECT_NEAR(expected_max_, result.second, 0.0000001f);
228 }
229 #endif
230
231 #if defined(ARCH_CPU_ARM_FAMILY) && defined(USE_NEON)
232 {
233 SCOPED_TRACE("EWMAAndMaxPower_NEON");
234 const std::pair<float, float>& result = vector_math::EWMAAndMaxPower_NEON(
235 initial_value_, data_.get(), data_len_, smoothing_factor_);
236 EXPECT_NEAR(expected_final_avg_, result.first, 0.0000001f);
237 EXPECT_NEAR(expected_max_, result.second, 0.0000001f);
238 }
239 #endif
240 }
241
242 private:
243 float initial_value_;
244 scoped_ptr_malloc<float, base::ScopedPtrAlignedFree> data_;
245 int data_len_;
246 float smoothing_factor_;
247 float expected_final_avg_;
248 float expected_max_;
249 };
250
251 } // namespace
252
253 typedef testing::TestWithParam<EWMATestScenario> VectorMathEWMAAndMaxPowerTest;
254
TEST_P(VectorMathEWMAAndMaxPowerTest,Correctness)255 TEST_P(VectorMathEWMAAndMaxPowerTest, Correctness) {
256 GetParam().RunTest();
257 }
258
259 static const float kZeros[] = { // 32 zeros
260 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
261 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
262 };
263
264 static const float kOnes[] = { // 32 ones
265 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
266 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
267 };
268
269 static const float kCheckerboard[] = { // 32 alternating 0, 1
270 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
271 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1
272 };
273
274 static const float kInverseCheckerboard[] = { // 32 alternating 1, 0
275 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
276 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0
277 };
278
279 INSTANTIATE_TEST_CASE_P(
280 Scenarios, VectorMathEWMAAndMaxPowerTest,
281 ::testing::Values(
282 // Zero-length input: Result should equal initial value.
283 EWMATestScenario(0.0f, NULL, 0, 0.0f).HasExpectedResult(0.0f, 0.0f),
284 EWMATestScenario(1.0f, NULL, 0, 0.0f).HasExpectedResult(1.0f, 0.0f),
285
286 // Smoothing factor of zero: Samples have no effect on result.
287 EWMATestScenario(0.0f, kOnes, 32, 0.0f).HasExpectedResult(0.0f, 1.0f),
288 EWMATestScenario(1.0f, kZeros, 32, 0.0f).HasExpectedResult(1.0f, 0.0f),
289
290 // Smothing factor of one: Result = last sample squared.
291 EWMATestScenario(0.0f, kCheckerboard, 32, 1.0f)
292 .ScaledBy(2.0f)
293 .HasExpectedResult(4.0f, 4.0f),
294 EWMATestScenario(1.0f, kInverseCheckerboard, 32, 1.0f)
295 .ScaledBy(2.0f)
296 .HasExpectedResult(0.0f, 4.0f),
297
298 // Smoothing factor of 1/4, muted signal.
299 EWMATestScenario(1.0f, kZeros, 1, 0.25f)
300 .HasExpectedResult(powf(0.75, 1.0f), 0.0f),
301 EWMATestScenario(1.0f, kZeros, 2, 0.25f)
302 .HasExpectedResult(powf(0.75, 2.0f), 0.0f),
303 EWMATestScenario(1.0f, kZeros, 3, 0.25f)
304 .HasExpectedResult(powf(0.75, 3.0f), 0.0f),
305 EWMATestScenario(1.0f, kZeros, 12, 0.25f)
306 .HasExpectedResult(powf(0.75, 12.0f), 0.0f),
307 EWMATestScenario(1.0f, kZeros, 13, 0.25f)
308 .HasExpectedResult(powf(0.75, 13.0f), 0.0f),
309 EWMATestScenario(1.0f, kZeros, 14, 0.25f)
310 .HasExpectedResult(powf(0.75, 14.0f), 0.0f),
311 EWMATestScenario(1.0f, kZeros, 15, 0.25f)
312 .HasExpectedResult(powf(0.75, 15.0f), 0.0f),
313
314 // Smoothing factor of 1/4, constant full-amplitude signal.
315 EWMATestScenario(0.0f, kOnes, 1, 0.25f).HasExpectedResult(0.25f, 1.0f),
316 EWMATestScenario(0.0f, kOnes, 2, 0.25f)
317 .HasExpectedResult(0.4375f, 1.0f),
318 EWMATestScenario(0.0f, kOnes, 3, 0.25f)
319 .HasExpectedResult(0.578125f, 1.0f),
320 EWMATestScenario(0.0f, kOnes, 12, 0.25f)
321 .HasExpectedResult(0.96832365f, 1.0f),
322 EWMATestScenario(0.0f, kOnes, 13, 0.25f)
323 .HasExpectedResult(0.97624274f, 1.0f),
324 EWMATestScenario(0.0f, kOnes, 14, 0.25f)
325 .HasExpectedResult(0.98218205f, 1.0f),
326 EWMATestScenario(0.0f, kOnes, 15, 0.25f)
327 .HasExpectedResult(0.98663654f, 1.0f),
328
329 // Smoothing factor of 1/4, checkerboard signal.
330 EWMATestScenario(0.0f, kCheckerboard, 1, 0.25f)
331 .HasExpectedResult(0.0f, 0.0f),
332 EWMATestScenario(0.0f, kCheckerboard, 2, 0.25f)
333 .HasExpectedResult(0.25f, 1.0f),
334 EWMATestScenario(0.0f, kCheckerboard, 3, 0.25f)
335 .HasExpectedResult(0.1875f, 1.0f),
336 EWMATestScenario(0.0f, kCheckerboard, 12, 0.25f)
337 .HasExpectedResult(0.55332780f, 1.0f),
338 EWMATestScenario(0.0f, kCheckerboard, 13, 0.25f)
339 .HasExpectedResult(0.41499585f, 1.0f),
340 EWMATestScenario(0.0f, kCheckerboard, 14, 0.25f)
341 .HasExpectedResult(0.56124689f, 1.0f),
342 EWMATestScenario(0.0f, kCheckerboard, 15, 0.25f)
343 .HasExpectedResult(0.42093517f, 1.0f),
344
345 // Smoothing factor of 1/4, inverse checkerboard signal.
346 EWMATestScenario(0.0f, kInverseCheckerboard, 1, 0.25f)
347 .HasExpectedResult(0.25f, 1.0f),
348 EWMATestScenario(0.0f, kInverseCheckerboard, 2, 0.25f)
349 .HasExpectedResult(0.1875f, 1.0f),
350 EWMATestScenario(0.0f, kInverseCheckerboard, 3, 0.25f)
351 .HasExpectedResult(0.390625f, 1.0f),
352 EWMATestScenario(0.0f, kInverseCheckerboard, 12, 0.25f)
353 .HasExpectedResult(0.41499585f, 1.0f),
354 EWMATestScenario(0.0f, kInverseCheckerboard, 13, 0.25f)
355 .HasExpectedResult(0.56124689f, 1.0f),
356 EWMATestScenario(0.0f, kInverseCheckerboard, 14, 0.25f)
357 .HasExpectedResult(0.42093517f, 1.0f),
358 EWMATestScenario(0.0f, kInverseCheckerboard, 15, 0.25f)
359 .HasExpectedResult(0.56570137f, 1.0f),
360
361 // Smoothing factor of 1/4, impluse signal.
362 EWMATestScenario(0.0f, kZeros, 3, 0.25f)
363 .WithImpulse(2.0f, 0)
364 .HasExpectedResult(0.562500f, 4.0f),
365 EWMATestScenario(0.0f, kZeros, 3, 0.25f)
366 .WithImpulse(2.0f, 1)
367 .HasExpectedResult(0.75f, 4.0f),
368 EWMATestScenario(0.0f, kZeros, 3, 0.25f)
369 .WithImpulse(2.0f, 2)
370 .HasExpectedResult(1.0f, 4.0f),
371 EWMATestScenario(0.0f, kZeros, 32, 0.25f)
372 .WithImpulse(2.0f, 0)
373 .HasExpectedResult(0.00013394f, 4.0f),
374 EWMATestScenario(0.0f, kZeros, 32, 0.25f)
375 .WithImpulse(2.0f, 1)
376 .HasExpectedResult(0.00017858f, 4.0f),
377 EWMATestScenario(0.0f, kZeros, 32, 0.25f)
378 .WithImpulse(2.0f, 2)
379 .HasExpectedResult(0.00023811f, 4.0f),
380 EWMATestScenario(0.0f, kZeros, 32, 0.25f)
381 .WithImpulse(2.0f, 3)
382 .HasExpectedResult(0.00031748f, 4.0f)
383 ));
384
385 } // namespace media
386