• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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