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
17using std::fill;
18
19namespace media {
20
21// Default test values.
22static const float kScale = 0.5;
23static const float kInputFillValue = 1.0;
24static const float kOutputFillValue = 3.0;
25static const int kVectorSize = 8192;
26
27class VectorMathTest : public testing::Test {
28 public:
29
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
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
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.
57TEST_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.
98TEST_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
138TEST_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
148class EWMATestScenario {
149 public:
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(...).
166  EWMATestScenario(const EWMATestScenario& other) { *this = 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
185  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
194  EWMATestScenario WithImpulse(float value, int offset) const {
195    EWMATestScenario result(*this);
196    result.data_.get()[offset] = value;
197    return result;
198  }
199
200  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
208  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
255typedef testing::TestWithParam<EWMATestScenario> VectorMathEWMAAndMaxPowerTest;
256
257TEST_P(VectorMathEWMAAndMaxPowerTest, Correctness) {
258  GetParam().RunTest();
259}
260
261static 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
266static 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
271static 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
276static 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
281INSTANTIATE_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