1 /*
2  *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
3  *
4  *  Use of this source code is governed by a BSD-style license
5  *  that can be found in the LICENSE file in the root of the source
6  *  tree. An additional intellectual property rights grant can be found
7  *  in the file PATENTS.  All contributing project authors may
8  *  be found in the AUTHORS file in the root of the source tree.
9  */
10 
11 #include "common_audio/smoothing_filter.h"
12 
13 #include <cmath>
14 #include <memory>
15 
16 #include "rtc_base/fake_clock.h"
17 #include "test/gtest.h"
18 
19 namespace webrtc {
20 
21 namespace {
22 
23 constexpr float kMaxAbsError = 1e-5f;
24 constexpr int64_t kClockInitialTime = 123456;
25 
26 struct SmoothingFilterStates {
SmoothingFilterStateswebrtc::__anonfa266f5e0111::SmoothingFilterStates27   explicit SmoothingFilterStates(int init_time_ms)
28       : smoothing_filter(init_time_ms) {
29     fake_clock.AdvanceTime(TimeDelta::Millis(kClockInitialTime));
30   }
31   rtc::ScopedFakeClock fake_clock;
32   SmoothingFilterImpl smoothing_filter;
33 };
34 
35 // This function does the following:
36 //   1. Add a sample to filter at current clock,
37 //   2. Advance the clock by |advance_time_ms|,
38 //   3. Get the output of both SmoothingFilter and verify that it equals to an
39 //      expected value.
CheckOutput(SmoothingFilterStates * states,float sample,int advance_time_ms,float expected_ouput)40 void CheckOutput(SmoothingFilterStates* states,
41                  float sample,
42                  int advance_time_ms,
43                  float expected_ouput) {
44   states->smoothing_filter.AddSample(sample);
45   states->fake_clock.AdvanceTime(TimeDelta::Millis(advance_time_ms));
46   auto output = states->smoothing_filter.GetAverage();
47   EXPECT_TRUE(output);
48   EXPECT_NEAR(expected_ouput, *output, kMaxAbsError);
49 }
50 
51 }  // namespace
52 
TEST(SmoothingFilterTest,NoOutputWhenNoSampleAdded)53 TEST(SmoothingFilterTest, NoOutputWhenNoSampleAdded) {
54   constexpr int kInitTimeMs = 100;
55   SmoothingFilterStates states(kInitTimeMs);
56   EXPECT_FALSE(states.smoothing_filter.GetAverage());
57 }
58 
59 // Python script to calculate the reference values used in this test.
60 //   import math
61 //
62 //   class ExpFilter:
63 //     def add_sample(self, new_value):
64 //       self.state = self.state * self.alpha + (1.0 - self.alpha) * new_value
65 //
66 //   filter = ExpFilter()
67 //   init_time = 795
68 //   init_factor = (1.0 / init_time) ** (1.0 / init_time)
69 //
70 //   filter.state = 1.0
71 //
72 //   for time_now in range(1, 500):
73 //     filter.alpha = math.exp(-init_factor ** time_now)
74 //     filter.add_sample(1.0)
75 //   print filter.state
76 //
77 //   for time_now in range(500, 600):
78 //     filter.alpha = math.exp(-init_factor ** time_now)
79 //     filter.add_sample(0.5)
80 //   print filter.state
81 //
82 //   for time_now in range(600, 700):
83 //     filter.alpha = math.exp(-init_factor ** time_now)
84 //     filter.add_sample(1.0)
85 //   print filter.state
86 //
87 //   for time_now in range(700, init_time):
88 //     filter.alpha = math.exp(-init_factor ** time_now)
89 //     filter.add_sample(1.0)
90 //
91 //   filter.alpha = math.exp(-1.0 / init_time)
92 //   for time_now in range(init_time, 800):
93 //     filter.add_sample(1.0)
94 //   print filter.state
95 //
96 //   for i in range(800, 900):
97 //     filter.add_sample(0.5)
98 //   print filter.state
99 //
100 //   for i in range(900, 1000):
101 //     filter.add_sample(1.0)
102 //   print filter.state
TEST(SmoothingFilterTest,CheckBehaviorAroundInitTime)103 TEST(SmoothingFilterTest, CheckBehaviorAroundInitTime) {
104   constexpr int kInitTimeMs = 795;
105   SmoothingFilterStates states(kInitTimeMs);
106   CheckOutput(&states, 1.0f, 500, 1.0f);
107   CheckOutput(&states, 0.5f, 100, 0.680562264029f);
108   CheckOutput(&states, 1.0f, 100, 0.794207139813f);
109   // Next step will go across initialization time.
110   CheckOutput(&states, 1.0f, 100, 0.829803409752f);
111   CheckOutput(&states, 0.5f, 100, 0.790821764210f);
112   CheckOutput(&states, 1.0f, 100, 0.815545922911f);
113 }
114 
TEST(SmoothingFilterTest,InitTimeEqualsZero)115 TEST(SmoothingFilterTest, InitTimeEqualsZero) {
116   constexpr int kInitTimeMs = 0;
117   SmoothingFilterStates states(kInitTimeMs);
118   CheckOutput(&states, 1.0f, 1, 1.0f);
119   CheckOutput(&states, 0.5f, 1, 0.5f);
120 }
121 
TEST(SmoothingFilterTest,InitTimeEqualsOne)122 TEST(SmoothingFilterTest, InitTimeEqualsOne) {
123   constexpr int kInitTimeMs = 1;
124   SmoothingFilterStates states(kInitTimeMs);
125   CheckOutput(&states, 1.0f, 1, 1.0f);
126   CheckOutput(&states, 0.5f, 1,
127               1.0f * std::exp(-1.0f) + (1.0f - std::exp(-1.0f)) * 0.5f);
128 }
129 
TEST(SmoothingFilterTest,GetAverageOutputsEmptyBeforeFirstSample)130 TEST(SmoothingFilterTest, GetAverageOutputsEmptyBeforeFirstSample) {
131   constexpr int kInitTimeMs = 100;
132   SmoothingFilterStates states(kInitTimeMs);
133   EXPECT_FALSE(states.smoothing_filter.GetAverage());
134   constexpr float kFirstSample = 1.2345f;
135   states.smoothing_filter.AddSample(kFirstSample);
136   EXPECT_EQ(kFirstSample, states.smoothing_filter.GetAverage());
137 }
138 
TEST(SmoothingFilterTest,CannotChangeTimeConstantDuringInitialization)139 TEST(SmoothingFilterTest, CannotChangeTimeConstantDuringInitialization) {
140   constexpr int kInitTimeMs = 100;
141   SmoothingFilterStates states(kInitTimeMs);
142   states.smoothing_filter.AddSample(0.0);
143 
144   // During initialization, |SetTimeConstantMs| does not take effect.
145   states.fake_clock.AdvanceTime(TimeDelta::Millis(kInitTimeMs - 1));
146   states.smoothing_filter.AddSample(0.0);
147 
148   EXPECT_FALSE(states.smoothing_filter.SetTimeConstantMs(kInitTimeMs * 2));
149   EXPECT_NE(std::exp(-1.0f / (kInitTimeMs * 2)),
150             states.smoothing_filter.alpha());
151 
152   states.fake_clock.AdvanceTime(TimeDelta::Millis(1));
153   states.smoothing_filter.AddSample(0.0);
154   // When initialization finishes, the time constant should be come
155   // |kInitTimeConstantMs|.
156   EXPECT_FLOAT_EQ(std::exp(-1.0f / kInitTimeMs),
157                   states.smoothing_filter.alpha());
158 
159   // After initialization, |SetTimeConstantMs| takes effect.
160   EXPECT_TRUE(states.smoothing_filter.SetTimeConstantMs(kInitTimeMs * 2));
161   EXPECT_FLOAT_EQ(std::exp(-1.0f / (kInitTimeMs * 2)),
162                   states.smoothing_filter.alpha());
163 }
164 
165 }  // namespace webrtc
166