Line data Source code
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 "webrtc/common_audio/smoothing_filter.h"
12 :
13 : #include <cmath>
14 :
15 : namespace webrtc {
16 :
17 0 : SmoothingFilterImpl::SmoothingFilterImpl(int init_time_ms, const Clock* clock)
18 : : init_time_ms_(init_time_ms),
19 : // Duing the initalization time, we use an increasing alpha. Specifically,
20 : // alpha(n) = exp(-powf(init_factor_, n)),
21 : // where |init_factor_| is chosen such that
22 : // alpha(init_time_ms_) = exp(-1.0f / init_time_ms_),
23 0 : init_factor_(init_time_ms_ == 0 ? 0.0f : powf(init_time_ms_,
24 0 : -1.0f / init_time_ms_)),
25 : // |init_const_| is to a factor to help the calculation during
26 : // initialization phase.
27 0 : init_const_(init_time_ms_ == 0
28 0 : ? 0.0f
29 0 : : init_time_ms_ -
30 0 : powf(init_time_ms_, 1.0f - 1.0f / init_time_ms_)),
31 0 : clock_(clock) {
32 0 : UpdateAlpha(init_time_ms_);
33 0 : }
34 :
35 : SmoothingFilterImpl::~SmoothingFilterImpl() = default;
36 :
37 0 : void SmoothingFilterImpl::AddSample(float sample) {
38 0 : const int64_t now_ms = clock_->TimeInMilliseconds();
39 :
40 0 : if (!init_end_time_ms_) {
41 : // This is equivalent to assuming the filter has been receiving the same
42 : // value as the first sample since time -infinity.
43 0 : state_ = last_sample_ = sample;
44 0 : init_end_time_ms_ = rtc::Optional<int64_t>(now_ms + init_time_ms_);
45 0 : last_state_time_ms_ = now_ms;
46 0 : return;
47 : }
48 :
49 0 : ExtrapolateLastSample(now_ms);
50 0 : last_sample_ = sample;
51 : }
52 :
53 0 : rtc::Optional<float> SmoothingFilterImpl::GetAverage() {
54 0 : if (!init_end_time_ms_) {
55 : // |init_end_time_ms_| undefined since we have not received any sample.
56 0 : return rtc::Optional<float>();
57 : }
58 0 : ExtrapolateLastSample(clock_->TimeInMilliseconds());
59 0 : return rtc::Optional<float>(state_);
60 : }
61 :
62 0 : bool SmoothingFilterImpl::SetTimeConstantMs(int time_constant_ms) {
63 0 : if (!init_end_time_ms_ || last_state_time_ms_ < *init_end_time_ms_) {
64 0 : return false;
65 : }
66 0 : UpdateAlpha(time_constant_ms);
67 0 : return true;
68 : }
69 :
70 0 : void SmoothingFilterImpl::UpdateAlpha(int time_constant_ms) {
71 0 : alpha_ = time_constant_ms == 0 ? 0.0f : exp(-1.0f / time_constant_ms);
72 0 : }
73 :
74 0 : void SmoothingFilterImpl::ExtrapolateLastSample(int64_t time_ms) {
75 0 : RTC_DCHECK_GE(time_ms, last_state_time_ms_);
76 0 : RTC_DCHECK(init_end_time_ms_);
77 :
78 0 : float multiplier = 0.0f;
79 :
80 0 : if (time_ms <= *init_end_time_ms_) {
81 : // Current update is to be made during initialization phase.
82 : // We update the state as if the |alpha| has been increased according
83 : // alpha(n) = exp(-powf(init_factor_, n)),
84 : // where n is the time (in millisecond) since the first sample received.
85 : // With algebraic derivation as shown in the Appendix, we can find that the
86 : // state can be updated in a similar manner as if alpha is a constant,
87 : // except for a different multiplier.
88 0 : if (init_time_ms_ == 0) {
89 : // This means |init_factor_| = 0.
90 0 : multiplier = 0.0f;
91 0 : } else if (init_time_ms_ == 1) {
92 : // This means |init_factor_| = 1.
93 0 : multiplier = exp(last_state_time_ms_ - time_ms);
94 : } else {
95 : multiplier =
96 0 : exp(-(powf(init_factor_, last_state_time_ms_ - *init_end_time_ms_) -
97 0 : powf(init_factor_, time_ms - *init_end_time_ms_)) /
98 0 : init_const_);
99 : }
100 : } else {
101 0 : if (last_state_time_ms_ < *init_end_time_ms_) {
102 : // The latest state update was made during initialization phase.
103 : // We first extrapolate to the initialization time.
104 0 : ExtrapolateLastSample(*init_end_time_ms_);
105 : // Then extrapolate the rest by the following.
106 : }
107 0 : multiplier = powf(alpha_, time_ms - last_state_time_ms_);
108 : }
109 :
110 0 : state_ = multiplier * state_ + (1.0f - multiplier) * last_sample_;
111 0 : last_state_time_ms_ = time_ms;
112 0 : }
113 :
114 : } // namespace webrtc
115 :
116 : // Appendix: derivation of extrapolation during initialization phase.
117 : // (LaTeX syntax)
118 : // Assuming
119 : // \begin{align}
120 : // y(n) &= \alpha_{n-1} y(n-1) + \left(1 - \alpha_{n-1}\right) x(m) \\*
121 : // &= \left(\prod_{i=m}^{n-1} \alpha_i\right) y(m) +
122 : // \left(1 - \prod_{i=m}^{n-1} \alpha_i \right) x(m)
123 : // \end{align}
124 : // Taking $\alpha_{n} = \exp(-\gamma^n)$, $\gamma$ denotes init\_factor\_, the
125 : // multiplier becomes
126 : // \begin{align}
127 : // \prod_{i=m}^{n-1} \alpha_i
128 : // &= \exp\left(-\sum_{i=m}^{n-1} \gamma^i \right) \\*
129 : // &= \begin{cases}
130 : // \exp\left(-\frac{\gamma^m - \gamma^n}{1 - \gamma} \right)
131 : // & \gamma \neq 1 \\*
132 : // m-n & \gamma = 1
133 : // \end{cases}
134 : // \end{align}
135 : // We know $\gamma = T^{-\frac{1}{T}}$, where $T$ denotes init\_time\_ms\_. Then
136 : // $1 - \gamma$ approaches zero when $T$ increases. This can cause numerical
137 : // difficulties. We multiply $T$ (if $T > 0$) to both numerator and denominator
138 : // in the fraction. See.
139 : // \begin{align}
140 : // \frac{\gamma^m - \gamma^n}{1 - \gamma}
141 : // &= \frac{T^\frac{T-m}{T} - T^\frac{T-n}{T}}{T - T^{1-\frac{1}{T}}}
142 : // \end{align}
|