Adding SmoothingFilter to audio network adaptor.

BUG=webrtc:6303

Review-Url: https://codereview.webrtc.org/2339523002
Cr-Commit-Position: refs/heads/master@{#14313}
diff --git a/webrtc/modules/BUILD.gn b/webrtc/modules/BUILD.gn
index 36ae692..4a565c1 100644
--- a/webrtc/modules/BUILD.gn
+++ b/webrtc/modules/BUILD.gn
@@ -245,6 +245,7 @@
       "audio_coding/audio_network_adaptor/dtx_controller_unittest.cc",
       "audio_coding/audio_network_adaptor/mock/mock_controller.h",
       "audio_coding/audio_network_adaptor/mock/mock_controller_manager.h",
+      "audio_coding/audio_network_adaptor/smoothing_filter_unittest.cc",
     ]
     deps = [
       "audio_coding:audio_network_adaptor",
diff --git a/webrtc/modules/audio_coding/BUILD.gn b/webrtc/modules/audio_coding/BUILD.gn
index 4fe49e5..2c90c7a 100644
--- a/webrtc/modules/audio_coding/BUILD.gn
+++ b/webrtc/modules/audio_coding/BUILD.gn
@@ -710,6 +710,8 @@
     "audio_network_adaptor/dtx_controller.cc",
     "audio_network_adaptor/dtx_controller.h",
     "audio_network_adaptor/include/audio_network_adaptor.h",
+    "audio_network_adaptor/smoothing_filter.cc",
+    "audio_network_adaptor/smoothing_filter.h",
   ]
   configs += [ "../..:common_config" ]
   public_configs = [ "../..:common_inherited_config" ]
diff --git a/webrtc/modules/audio_coding/audio_network_adaptor/audio_network_adaptor.gypi b/webrtc/modules/audio_coding/audio_network_adaptor/audio_network_adaptor.gypi
index 8e87815..2a591c3 100644
--- a/webrtc/modules/audio_coding/audio_network_adaptor/audio_network_adaptor.gypi
+++ b/webrtc/modules/audio_coding/audio_network_adaptor/audio_network_adaptor.gypi
@@ -24,7 +24,9 @@
         'controller_manager.h',
         'dtx_controller.h',
         'dtx_controller.cc',
-        'include/audio_network_adaptor.h'
+        'include/audio_network_adaptor.h',
+        'smoothing_filter.h',
+        'smoothing_filter.cc',
       ], # source
     },
   ], # targets
diff --git a/webrtc/modules/audio_coding/audio_network_adaptor/smoothing_filter.cc b/webrtc/modules/audio_coding/audio_network_adaptor/smoothing_filter.cc
new file mode 100644
index 0000000..8a81069
--- /dev/null
+++ b/webrtc/modules/audio_coding/audio_network_adaptor/smoothing_filter.cc
@@ -0,0 +1,62 @@
+/*
+ *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <cmath>
+
+#include "webrtc/modules/audio_coding/audio_network_adaptor/smoothing_filter.h"
+
+namespace webrtc {
+
+SmoothingFilterImpl::SmoothingFilterImpl(int time_constant_ms,
+                                         const Clock* clock)
+    : time_constant_ms_(time_constant_ms),
+      clock_(clock),
+      first_sample_received_(false),
+      initialized_(false),
+      first_sample_time_ms_(0),
+      last_sample_time_ms_(0),
+      filter_(0.0) {}
+
+void SmoothingFilterImpl::AddSample(float sample) {
+  if (!first_sample_received_) {
+    last_sample_time_ms_ = first_sample_time_ms_ = clock_->TimeInMilliseconds();
+    first_sample_received_ = true;
+    RTC_DCHECK_EQ(rtc::ExpFilter::kValueUndefined, filter_.filtered());
+
+    // Since this is first sample, any value for argument 1 should work.
+    filter_.Apply(0.0f, sample);
+    return;
+  }
+
+  int64_t now_ms = clock_->TimeInMilliseconds();
+  if (!initialized_) {
+    float duration = now_ms - first_sample_time_ms_;
+    if (duration < static_cast<int64_t>(time_constant_ms_)) {
+      filter_.UpdateBase(exp(1.0f / duration));
+    } else {
+      initialized_ = true;
+      filter_.UpdateBase(exp(1.0f / time_constant_ms_));
+    }
+  }
+
+  // The filter will do the following:
+  //   float alpha = pow(base, last_update_time_ms_ - now_ms);
+  //   filtered_ = alpha * filtered_ + (1 - alpha) * sample;
+  filter_.Apply(static_cast<float>(last_sample_time_ms_ - now_ms), sample);
+  last_sample_time_ms_ = now_ms;
+}
+
+rtc::Optional<float> SmoothingFilterImpl::GetAverage() const {
+  float value = filter_.filtered();
+  return value == rtc::ExpFilter::kValueUndefined ? rtc::Optional<float>()
+                                                  : rtc::Optional<float>(value);
+}
+
+}  // namespace webrtc
diff --git a/webrtc/modules/audio_coding/audio_network_adaptor/smoothing_filter.h b/webrtc/modules/audio_coding/audio_network_adaptor/smoothing_filter.h
new file mode 100644
index 0000000..c4de7b5
--- /dev/null
+++ b/webrtc/modules/audio_coding/audio_network_adaptor/smoothing_filter.h
@@ -0,0 +1,54 @@
+/*
+ *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef WEBRTC_MODULES_AUDIO_CODING_AUDIO_NETWORK_ADAPTOR_SMOOTHING_FILTER_H_
+#define WEBRTC_MODULES_AUDIO_CODING_AUDIO_NETWORK_ADAPTOR_SMOOTHING_FILTER_H_
+
+#include "webrtc/base/constructormagic.h"
+#include "webrtc/base/exp_filter.h"
+#include "webrtc/base/optional.h"
+#include "webrtc/system_wrappers/include/clock.h"
+
+namespace webrtc {
+
+class SmoothingFilter {
+ public:
+  virtual ~SmoothingFilter() = default;
+  virtual void AddSample(float sample) = 0;
+  virtual rtc::Optional<float> GetAverage() const = 0;
+};
+
+// SmoothingFilterImpl applies an exponential filter
+//   alpha = exp(-sample_interval / time_constant);
+//   y[t] = alpha * y[t-1] + (1 - alpha) * sample;
+class SmoothingFilterImpl final : public SmoothingFilter {
+ public:
+  // |time_constant_ms| is the time constant for the exponential filter.
+  SmoothingFilterImpl(int time_constant_ms, const Clock* clock);
+
+  void AddSample(float sample) override;
+  rtc::Optional<float> GetAverage() const override;
+
+ private:
+  const int time_constant_ms_;
+  const Clock* const clock_;
+
+  bool first_sample_received_;
+  bool initialized_;
+  int64_t first_sample_time_ms_;
+  int64_t last_sample_time_ms_;
+  rtc::ExpFilter filter_;
+
+  RTC_DISALLOW_COPY_AND_ASSIGN(SmoothingFilterImpl);
+};
+
+}  // namespace webrtc
+
+#endif  // WEBRTC_MODULES_AUDIO_CODING_AUDIO_NETWORK_ADAPTOR_SMOOTHING_FILTER_H_
diff --git a/webrtc/modules/audio_coding/audio_network_adaptor/smoothing_filter_unittest.cc b/webrtc/modules/audio_coding/audio_network_adaptor/smoothing_filter_unittest.cc
new file mode 100644
index 0000000..388ae4f
--- /dev/null
+++ b/webrtc/modules/audio_coding/audio_network_adaptor/smoothing_filter_unittest.cc
@@ -0,0 +1,108 @@
+/*
+ *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <memory>
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webrtc/modules/audio_coding/audio_network_adaptor/smoothing_filter.h"
+
+namespace webrtc {
+
+namespace {
+
+constexpr int kTimeConstantMs = 1000;
+constexpr float kMaxAbsError = 0.0001f;
+constexpr int64_t kClockInitialTime = 123456;
+
+struct SmoothingFilterStates {
+  std::unique_ptr<SimulatedClock> simulated_clock;
+  std::unique_ptr<SmoothingFilter> smoothing_filter;
+};
+
+SmoothingFilterStates CreateSmoothingFilter() {
+  SmoothingFilterStates states;
+  states.simulated_clock.reset(new SimulatedClock(kClockInitialTime));
+  states.smoothing_filter.reset(
+      new SmoothingFilterImpl(kTimeConstantMs, states.simulated_clock.get()));
+  return states;
+}
+
+void CheckOutput(SmoothingFilterStates* states,
+                 int advance_time_ms,
+                 float sample,
+                 float expected_ouput) {
+  states->simulated_clock->AdvanceTimeMilliseconds(advance_time_ms);
+  states->smoothing_filter->AddSample(sample);
+  auto output = states->smoothing_filter->GetAverage();
+  EXPECT_TRUE(output);
+  EXPECT_NEAR(expected_ouput, *output, kMaxAbsError);
+}
+
+}  // namespace
+
+TEST(SmoothingFilterTest, NoOutputWhenNoSampleAdded) {
+  auto states = CreateSmoothingFilter();
+  EXPECT_FALSE(states.smoothing_filter->GetAverage());
+}
+
+// Python script to calculate the reference values used in this test.
+//  import math
+//
+//  class ExpFilter:
+//    alpha = 0.0
+//    old_value = 0.0
+//    def calc(self, new_value):
+//      self.old_value = self.old_value * self.alpha
+//                       + (1.0 - self.alpha) * new_value
+//      return self.old_value
+//
+//  delta_t = 100.0
+//  filter = ExpFilter()
+//  total_t = 100.0
+//  filter.alpha = math.exp(-delta_t/ total_t)
+//  print filter.calc(1.0)
+//  total_t = 200.0
+//  filter.alpha = math.exp(-delta_t/ total_t)
+//  print filter.calc(0.0)
+//  total_t = 300.0
+//  filter.alpha = math.exp(-delta_t/ total_t)
+//  print filter.calc(1.0)
+TEST(SmoothingFilterTest, CheckBehaviorBeforeInitialized) {
+  // Adding three samples, all added before |kTimeConstantMs| is reached.
+  constexpr int kTimeIntervalMs = 100;
+  auto states = CreateSmoothingFilter();
+  states.smoothing_filter->AddSample(0.0);
+  CheckOutput(&states, kTimeIntervalMs, 1.0, 0.63212f);
+  CheckOutput(&states, kTimeIntervalMs, 0.0, 0.38340f);
+  CheckOutput(&states, kTimeIntervalMs, 1.0, 0.55818f);
+}
+
+// Python script to calculate the reference value used in this test.
+// (after defining ExpFilter as for CheckBehaviorBeforeInitialized)
+//  time_constant_ms = 1000.0
+//  filter = ExpFilter()
+//  delta_t = 1100.0
+//  filter.alpha = math.exp(-delta_t/ time_constant_ms)
+//  print filter.calc(1.0)
+//  delta_t = 100.0
+//  filter.alpha = math.exp(-delta_t/ time_constant_ms)
+//  print filter.calc(0.0)
+//  print filter.calc(1.0)
+TEST(SmoothingFilterTest, CheckBehaviorAfterInitialized) {
+  constexpr int kTimeIntervalMs = 100;
+  auto states = CreateSmoothingFilter();
+  states.smoothing_filter->AddSample(0.0);
+  states.simulated_clock->AdvanceTimeMilliseconds(kTimeConstantMs);
+  CheckOutput(&states, kTimeIntervalMs, 1.0, 0.66713f);
+  CheckOutput(&states, kTimeIntervalMs, 0.0, 0.60364f);
+  CheckOutput(&states, kTimeIntervalMs, 1.0, 0.64136f);
+}
+
+}  // namespace webrtc