OLD | NEW |
1 /* | 1 /* |
2 * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. | 2 * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license | 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 | 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 | 6 * tree. An additional intellectual property rights grant can be found |
7 * in the file PATENTS. All contributing project authors may | 7 * in the file PATENTS. All contributing project authors may |
8 * be found in the AUTHORS file in the root of the source tree. | 8 * be found in the AUTHORS file in the root of the source tree. |
9 */ | 9 */ |
10 | 10 |
| 11 #include "webrtc/common_audio/smoothing_filter.h" |
| 12 |
11 #include <cmath> | 13 #include <cmath> |
12 | 14 |
13 #include "webrtc/common_audio/smoothing_filter.h" | |
14 | |
15 namespace webrtc { | 15 namespace webrtc { |
16 | 16 |
17 SmoothingFilterImpl::SmoothingFilterImpl(int time_constant_ms, | 17 SmoothingFilterImpl::SmoothingFilterImpl(int init_time_ms_, const Clock* clock) |
18 const Clock* clock) | 18 : init_time_ms_(init_time_ms_), |
19 : time_constant_ms_(time_constant_ms), | 19 // Duing the initalization time, we use an increasing alpha. Specifically, |
20 clock_(clock), | 20 // alpha(n) = exp(pow(init_factor_, n)), |
21 first_sample_received_(false), | 21 // where |init_factor_| is chosen such that |
22 initialized_(false), | 22 // alpha(init_time_ms_) = exp(-1.0f / init_time_ms_), |
23 first_sample_time_ms_(0), | 23 init_factor_(pow(init_time_ms_, 1.0f / init_time_ms_)), |
24 last_sample_time_ms_(0), | 24 // |init_const_| is to a factor to help the calculation during |
25 filter_(0.0) {} | 25 // initialization phase. |
| 26 init_const_(1.0f / (init_time_ms_ - |
| 27 pow(init_time_ms_, 1.0f - 1.0f / init_time_ms_))), |
| 28 clock_(clock) { |
| 29 UpdateAlpha(init_time_ms_); |
| 30 } |
| 31 |
| 32 SmoothingFilterImpl::~SmoothingFilterImpl() = default; |
26 | 33 |
27 void SmoothingFilterImpl::AddSample(float sample) { | 34 void SmoothingFilterImpl::AddSample(float sample) { |
28 if (!first_sample_received_) { | 35 const int64_t now_ms = clock_->TimeInMilliseconds(); |
29 last_sample_time_ms_ = first_sample_time_ms_ = clock_->TimeInMilliseconds(); | |
30 first_sample_received_ = true; | |
31 RTC_DCHECK_EQ(rtc::ExpFilter::kValueUndefined, filter_.filtered()); | |
32 | 36 |
33 // Since this is first sample, any value for argument 1 should work. | 37 if (!first_sample_time_ms_) { |
34 filter_.Apply(0.0f, sample); | 38 // This is equivalent to assuming the filter has been receiving the same |
| 39 // value as the first sample since time -infinity. |
| 40 state_ = last_sample_ = sample; |
| 41 first_sample_time_ms_ = rtc::Optional<int64_t>(now_ms); |
| 42 last_state_time_ms_ = now_ms; |
35 return; | 43 return; |
36 } | 44 } |
37 | 45 |
38 int64_t now_ms = clock_->TimeInMilliseconds(); | 46 ExtrapolateLastSample(now_ms); |
39 if (!initialized_) { | 47 last_sample_ = sample; |
40 float duration = now_ms - first_sample_time_ms_; | 48 } |
41 if (duration < static_cast<int64_t>(time_constant_ms_)) { | 49 |
42 filter_.UpdateBase(exp(1.0f / duration)); | 50 rtc::Optional<float> SmoothingFilterImpl::GetAverage() { |
43 } else { | 51 if (!first_sample_time_ms_) |
44 initialized_ = true; | 52 return rtc::Optional<float>(); |
45 filter_.UpdateBase(exp(1.0f / time_constant_ms_)); | 53 ExtrapolateLastSample(clock_->TimeInMilliseconds()); |
| 54 return rtc::Optional<float>(state_); |
| 55 } |
| 56 |
| 57 bool SmoothingFilterImpl::SetTimeConstantMs(int time_constant_ms) { |
| 58 if (!first_sample_time_ms_ || |
| 59 last_state_time_ms_ < *first_sample_time_ms_ + init_time_ms_) { |
| 60 return false; |
| 61 } |
| 62 UpdateAlpha(time_constant_ms); |
| 63 return true; |
| 64 } |
| 65 |
| 66 void SmoothingFilterImpl::UpdateAlpha(int time_constant_ms) { |
| 67 alpha_ = exp(-1.0f / time_constant_ms); |
| 68 } |
| 69 |
| 70 void SmoothingFilterImpl::ExtrapolateLastSample(int64_t time_ms) { |
| 71 RTC_DCHECK_GE(time_ms, last_state_time_ms_); |
| 72 RTC_DCHECK(first_sample_time_ms_); |
| 73 |
| 74 float multiplier = 0.0f; |
| 75 if (time_ms <= *first_sample_time_ms_ + init_time_ms_) { |
| 76 // Current update is to be made during initialization phase. |
| 77 // We update the state as if the |alpha| has been increased according |
| 78 // alpha(n) = exp(pow(init_factor_, n)), |
| 79 // where n is the time (in millisecond) since the first sample received. |
| 80 // With algebraic derivation as shown in the Appendix, we can find that the |
| 81 // state can be updated in a similar manner as if alpha is a constant, |
| 82 // except for a different multiplier. |
| 83 multiplier = exp(-init_const_ * |
| 84 (pow(init_factor_, |
| 85 *first_sample_time_ms_ + init_time_ms_ - last_state_time_ms_) - |
| 86 pow(init_factor_, *first_sample_time_ms_ + init_time_ms_ - time_ms))); |
| 87 } else { |
| 88 if (last_state_time_ms_ < *first_sample_time_ms_ + init_time_ms_) { |
| 89 // The latest state update was made during initialization phase. |
| 90 // We first extrapolate to the initialization time. |
| 91 ExtrapolateLastSample(*first_sample_time_ms_ + init_time_ms_); |
| 92 // Then extrapolate the rest by the following. |
46 } | 93 } |
| 94 multiplier = pow(alpha_, time_ms - last_state_time_ms_); |
47 } | 95 } |
48 | 96 |
49 // The filter will do the following: | 97 state_ = multiplier * state_ + (1.0f - multiplier) * last_sample_; |
50 // float alpha = pow(base, last_update_time_ms_ - now_ms); | 98 last_state_time_ms_ = time_ms; |
51 // filtered_ = alpha * filtered_ + (1 - alpha) * sample; | |
52 filter_.Apply(static_cast<float>(last_sample_time_ms_ - now_ms), sample); | |
53 last_sample_time_ms_ = now_ms; | |
54 } | |
55 | |
56 rtc::Optional<float> SmoothingFilterImpl::GetAverage() const { | |
57 float value = filter_.filtered(); | |
58 return value == rtc::ExpFilter::kValueUndefined ? rtc::Optional<float>() | |
59 : rtc::Optional<float>(value); | |
60 } | |
61 | |
62 void SmoothingFilterImpl::SetTimeConstantMs(int time_constant_ms) { | |
63 time_constant_ms_ = time_constant_ms; | |
64 filter_.UpdateBase(exp(1.0f / time_constant_ms_)); | |
65 } | 99 } |
66 | 100 |
67 } // namespace webrtc | 101 } // namespace webrtc |
| 102 |
| 103 // Appendix: derivation of extrapolation during initialization phase. |
| 104 // (LaTeX syntax) |
| 105 // Assuming |
| 106 // \begin{align} |
| 107 // y(n) &= \alpha_{n-1} y(n-1) + \left(1 - \alpha_{n-1}\right) x(m) \\* |
| 108 // &= \left(\prod_{i=m}^{n-1} \alpha_i\right) y(m) + |
| 109 // \left(1 - \prod_{i=m}^{n-1} \alpha_i \right) x(m) |
| 110 // \end{align} |
| 111 // Taking $\alpha_{n} = \exp{\gamma^n}$, $\gamma$ denotes init\_factor\_, the |
| 112 // multiplier becomes |
| 113 // \begin{align} |
| 114 // \prod_{i=m}^{n-1} \alpha_i |
| 115 // &= \exp\left(\prod_{i=m}^{n-1} \gamma^i \right) \\* |
| 116 // &= \exp\left(\frac{\gamma^m - \gamma^n}{1 - \gamma} \right) |
| 117 // \end{align} |
| 118 // We know $\gamma = T^\frac{1}{T}$, where $T$ denotes init\_time\_ms\_. Then |
| 119 // $1 - \gamma$ approaches zero when $T$ increases. This can cause numerical |
| 120 // difficulties. We multiply $T$ to both numerator and denominator in the |
| 121 // fraction. See. |
| 122 // \begin{align} |
| 123 // \frac{\gamma^m - \gamma^n}{1 - \gamma} |
| 124 // &= \frac{T^\frac{T-m}{T} - T^\frac{T-n}{T}}{T - T^{1-\frac{1}{T}}} |
| 125 // \end{align} |
OLD | NEW |