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