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