OLD | NEW |
1 /* | 1 /* |
2 * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. | 2 * Copyright (c) 2017 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/modules/audio_processing/aec3/aec_state.h" | 11 #include "webrtc/modules/audio_processing/aec3/aec_state.h" |
12 | 12 |
13 #include <math.h> | 13 #include <math.h> |
14 #include <numeric> | 14 #include <numeric> |
15 #include <vector> | 15 #include <vector> |
16 | 16 |
17 #include "webrtc/base/array_view.h" | 17 #include "webrtc/base/array_view.h" |
18 #include "webrtc/base/atomicops.h" | 18 #include "webrtc/base/atomicops.h" |
19 #include "webrtc/base/checks.h" | 19 #include "webrtc/base/checks.h" |
20 #include "webrtc/modules/audio_processing/logging/apm_data_dumper.h" | 20 #include "webrtc/modules/audio_processing/logging/apm_data_dumper.h" |
21 | 21 |
22 namespace webrtc { | 22 namespace webrtc { |
23 namespace { | 23 namespace { |
24 | 24 |
25 constexpr size_t kEchoPathChangeConvergenceBlocks = 4 * kNumBlocksPerSecond; | 25 constexpr size_t kEchoPathChangeConvergenceBlocks = 2 * kNumBlocksPerSecond; |
26 constexpr size_t kSaturationLeakageBlocks = 20; | 26 constexpr size_t kSaturationLeakageBlocks = 20; |
27 | 27 |
28 // Computes delay of the adaptive filter. | 28 // Computes delay of the adaptive filter. |
29 rtc::Optional<size_t> EstimateFilterDelay( | 29 rtc::Optional<size_t> EstimateFilterDelay( |
30 const std::vector<std::array<float, kFftLengthBy2Plus1>>& | 30 const std::vector<std::array<float, kFftLengthBy2Plus1>>& |
31 adaptive_filter_frequency_response) { | 31 adaptive_filter_frequency_response) { |
32 const auto& H2 = adaptive_filter_frequency_response; | 32 const auto& H2 = adaptive_filter_frequency_response; |
33 | 33 |
34 size_t reliable_delays_sum = 0; | 34 size_t reliable_delays_sum = 0; |
35 size_t num_reliable_delays = 0; | 35 size_t num_reliable_delays = 0; |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
82 : data_dumper_( | 82 : data_dumper_( |
83 new ApmDataDumper(rtc::AtomicOps::Increment(&instance_count_))), | 83 new ApmDataDumper(rtc::AtomicOps::Increment(&instance_count_))), |
84 echo_path_change_counter_(kEchoPathChangeCounterInitial) {} | 84 echo_path_change_counter_(kEchoPathChangeCounterInitial) {} |
85 | 85 |
86 AecState::~AecState() = default; | 86 AecState::~AecState() = default; |
87 | 87 |
88 void AecState::HandleEchoPathChange( | 88 void AecState::HandleEchoPathChange( |
89 const EchoPathVariability& echo_path_variability) { | 89 const EchoPathVariability& echo_path_variability) { |
90 if (echo_path_variability.AudioPathChanged()) { | 90 if (echo_path_variability.AudioPathChanged()) { |
91 blocks_since_last_saturation_ = 0; | 91 blocks_since_last_saturation_ = 0; |
92 active_render_blocks_ = 0; | |
93 usable_linear_estimate_ = false; | 92 usable_linear_estimate_ = false; |
94 echo_leakage_detected_ = false; | 93 echo_leakage_detected_ = false; |
95 capture_signal_saturation_ = false; | 94 capture_signal_saturation_ = false; |
96 echo_saturation_ = false; | 95 echo_saturation_ = false; |
97 previous_max_sample_ = 0.f; | 96 previous_max_sample_ = 0.f; |
98 | 97 |
99 if (echo_path_variability.delay_change) { | 98 if (echo_path_variability.delay_change) { |
100 force_zero_gain_counter_ = 0; | 99 force_zero_gain_counter_ = 0; |
| 100 blocks_with_filter_adaptation_ = 0; |
| 101 render_received_ = false; |
101 force_zero_gain_ = true; | 102 force_zero_gain_ = true; |
102 echo_path_change_counter_ = kEchoPathChangeCounterMax; | 103 echo_path_change_counter_ = kEchoPathChangeCounterMax; |
103 } | 104 } |
104 if (echo_path_variability.gain_change) { | 105 if (echo_path_variability.gain_change) { |
105 echo_path_change_counter_ = kEchoPathChangeCounterInitial; | 106 echo_path_change_counter_ = kEchoPathChangeCounterInitial; |
106 } | 107 } |
107 } | 108 } |
108 } | 109 } |
109 | 110 |
110 void AecState::Update(const std::vector<std::array<float, kFftLengthBy2Plus1>>& | 111 void AecState::Update(const std::vector<std::array<float, kFftLengthBy2Plus1>>& |
111 adaptive_filter_frequency_response, | 112 adaptive_filter_frequency_response, |
112 const rtc::Optional<size_t>& external_delay_samples, | 113 const rtc::Optional<size_t>& external_delay_samples, |
113 const RenderBuffer& render_buffer, | 114 const RenderBuffer& render_buffer, |
114 const std::array<float, kFftLengthBy2Plus1>& E2_main, | 115 const std::array<float, kFftLengthBy2Plus1>& E2_main, |
115 const std::array<float, kFftLengthBy2Plus1>& Y2, | 116 const std::array<float, kFftLengthBy2Plus1>& Y2, |
116 rtc::ArrayView<const float> x, | 117 rtc::ArrayView<const float> x, |
117 bool echo_leakage_detected) { | 118 bool echo_leakage_detected) { |
118 // Store input parameters. | 119 // Store input parameters. |
119 echo_leakage_detected_ = echo_leakage_detected; | 120 echo_leakage_detected_ = echo_leakage_detected; |
120 | 121 |
121 // Update counters. | 122 // Update counters. |
122 const float x_energy = std::inner_product(x.begin(), x.end(), x.begin(), 0.f); | 123 const float x_energy = std::inner_product(x.begin(), x.end(), x.begin(), 0.f); |
123 const bool active_render_block = x_energy > 10000.f * kFftLengthBy2; | 124 const bool active_render_block = x_energy > 10000.f * kFftLengthBy2; |
124 active_render_blocks_ += active_render_block ? 1 : 0; | 125 if (active_render_block) { |
| 126 render_received_ = true; |
| 127 } |
| 128 blocks_with_filter_adaptation_ += |
| 129 (active_render_block && (!SaturatedCapture()) ? 1 : 0); |
125 --echo_path_change_counter_; | 130 --echo_path_change_counter_; |
126 | 131 |
127 // Force zero echo suppression gain after an echo path change to allow at | 132 // Force zero echo suppression gain after an echo path change to allow at |
128 // least some render data to be collected in order to avoid an initial echo | 133 // least some render data to be collected in order to avoid an initial echo |
129 // burst. | 134 // burst. |
130 constexpr size_t kZeroGainBlocksAfterChange = kNumBlocksPerSecond / 5; | 135 constexpr size_t kZeroGainBlocksAfterChange = kNumBlocksPerSecond / 5; |
131 force_zero_gain_ = (++force_zero_gain_counter_) < kZeroGainBlocksAfterChange; | 136 force_zero_gain_ = (++force_zero_gain_counter_) < kZeroGainBlocksAfterChange; |
132 | 137 |
133 // Estimate delays. | 138 // Estimate delays. |
134 filter_delay_ = EstimateFilterDelay(adaptive_filter_frequency_response); | 139 filter_delay_ = EstimateFilterDelay(adaptive_filter_frequency_response); |
135 external_delay_ = | 140 external_delay_ = |
136 external_delay_samples | 141 external_delay_samples |
137 ? rtc::Optional<size_t>(*external_delay_samples / kBlockSize) | 142 ? rtc::Optional<size_t>(*external_delay_samples / kBlockSize) |
138 : rtc::Optional<size_t>(); | 143 : rtc::Optional<size_t>(); |
139 | 144 |
140 // Update the ERL and ERLE measures. | 145 // Update the ERL and ERLE measures. |
141 if (filter_delay_ && echo_path_change_counter_ <= 0) { | 146 if (filter_delay_ && echo_path_change_counter_ <= 0) { |
142 const auto& X2 = render_buffer.Spectrum(*filter_delay_); | 147 const auto& X2 = render_buffer.Spectrum(*filter_delay_); |
143 erle_estimator_.Update(X2, Y2, E2_main); | 148 erle_estimator_.Update(X2, Y2, E2_main); |
144 erl_estimator_.Update(X2, Y2); | 149 erl_estimator_.Update(X2, Y2); |
145 } | 150 } |
146 | 151 |
147 // Detect and flag echo saturation. | 152 // Detect and flag echo saturation. |
| 153 // TODO(peah): Add the delay in this computation to ensure that the render and |
| 154 // capture signals are properly aligned. |
148 RTC_DCHECK_LT(0, x.size()); | 155 RTC_DCHECK_LT(0, x.size()); |
149 const float max_sample = fabs(*std::max_element( | 156 const float max_sample = fabs(*std::max_element( |
150 x.begin(), x.end(), [](float a, float b) { return a * a < b * b; })); | 157 x.begin(), x.end(), [](float a, float b) { return a * a < b * b; })); |
151 const bool saturated_echo = | 158 const bool saturated_echo = |
152 previous_max_sample_ * kFixedEchoPathGain > 1600 && SaturatedCapture(); | 159 previous_max_sample_ * kFixedEchoPathGain > 1600 && SaturatedCapture(); |
153 previous_max_sample_ = max_sample; | 160 previous_max_sample_ = max_sample; |
154 | 161 |
155 // Counts the blocks since saturation. | 162 // Counts the blocks since saturation. |
156 blocks_since_last_saturation_ = | 163 blocks_since_last_saturation_ = |
157 saturated_echo ? 0 : blocks_since_last_saturation_ + 1; | 164 saturated_echo ? 0 : blocks_since_last_saturation_ + 1; |
158 echo_saturation_ = blocks_since_last_saturation_ < kSaturationLeakageBlocks; | 165 echo_saturation_ = blocks_since_last_saturation_ < kSaturationLeakageBlocks; |
159 | 166 |
160 // Flag whether the linear filter estimate is usable. | 167 // Flag whether the linear filter estimate is usable. |
161 usable_linear_estimate_ = | 168 usable_linear_estimate_ = |
162 (!echo_saturation_) && | 169 (!echo_saturation_) && |
163 active_render_blocks_ > kEchoPathChangeConvergenceBlocks && | 170 (!render_received_ || |
| 171 blocks_with_filter_adaptation_ > kEchoPathChangeConvergenceBlocks) && |
164 filter_delay_ && echo_path_change_counter_ <= 0; | 172 filter_delay_ && echo_path_change_counter_ <= 0; |
165 | 173 |
166 // After an amount of active render samples for which an echo should have been | 174 // After an amount of active render samples for which an echo should have been |
167 // detected in the capture signal if the ERL was not infinite, flag that a | 175 // detected in the capture signal if the ERL was not infinite, flag that a |
168 // headset is used. | 176 // headset is used. |
169 headset_detected_ = !external_delay_ && !filter_delay_ && | 177 headset_detected_ = |
170 active_render_blocks_ >= kEchoPathChangeConvergenceBlocks; | 178 !external_delay_ && !filter_delay_ && |
| 179 (!render_received_ || |
| 180 blocks_with_filter_adaptation_ >= kEchoPathChangeConvergenceBlocks); |
171 } | 181 } |
172 | 182 |
173 } // namespace webrtc | 183 } // namespace webrtc |
OLD | NEW |