blob: e2a5b998a48e6942074ee44cc358ebe3d14fdb9e [file] [log] [blame]
pbos@webrtc.org788acd12014-12-15 09:41:24 +00001/*
2 * Copyright (c) 2013 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
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020011#include "modules/audio_processing/agc/agc_manager_direct.h"
pbos@webrtc.org788acd12014-12-15 09:41:24 +000012
Yves Gerey988cc082018-10-23 12:03:01 +020013#include <algorithm>
pbos@webrtc.org788acd12014-12-15 09:41:24 +000014#include <cmath>
15
Per Åhgren928146f2019-08-20 09:19:21 +020016#include "common_audio/include/audio_util.h"
Sam Zackrisson41478c72019-10-15 10:10:26 +020017#include "modules/audio_processing/agc/gain_control.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020018#include "modules/audio_processing/agc/gain_map_internal.h"
Hanna Silena0047152021-06-03 03:29:38 +020019#include "modules/audio_processing/include/audio_frame_view.h"
Per Åhgren3daedb62019-11-22 12:11:40 +010020#include "rtc_base/atomic_ops.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020021#include "rtc_base/checks.h"
22#include "rtc_base/logging.h"
Karl Wiberge40468b2017-11-22 10:42:26 +010023#include "rtc_base/numerics/safe_minmax.h"
henrikaebf45522019-11-04 13:59:21 +010024#include "system_wrappers/include/field_trial.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020025#include "system_wrappers/include/metrics.h"
pbos@webrtc.org788acd12014-12-15 09:41:24 +000026
27namespace webrtc {
28
29namespace {
30
pbos@webrtc.org788acd12014-12-15 09:41:24 +000031// Amount of error we tolerate in the microphone level (presumably due to OS
32// quantization) before we assume the user has manually adjusted the microphone.
Hanna Silenb8dc7fa2021-05-20 17:37:56 +020033constexpr int kLevelQuantizationSlack = 25;
pbos@webrtc.org788acd12014-12-15 09:41:24 +000034
Hanna Silenb8dc7fa2021-05-20 17:37:56 +020035constexpr int kDefaultCompressionGain = 7;
36constexpr int kMaxCompressionGain = 12;
37constexpr int kMinCompressionGain = 2;
pbos@webrtc.org788acd12014-12-15 09:41:24 +000038// Controls the rate of compression changes towards the target.
Hanna Silenb8dc7fa2021-05-20 17:37:56 +020039constexpr float kCompressionGainStep = 0.05f;
pbos@webrtc.org788acd12014-12-15 09:41:24 +000040
Hanna Silenb8dc7fa2021-05-20 17:37:56 +020041constexpr int kMaxMicLevel = 255;
kwiberg@webrtc.org2ebfac52015-01-14 10:51:54 +000042static_assert(kGainMapSize > kMaxMicLevel, "gain map too small");
Hanna Silenb8dc7fa2021-05-20 17:37:56 +020043constexpr int kMinMicLevel = 12;
pbos@webrtc.org788acd12014-12-15 09:41:24 +000044
45// Prevent very large microphone level changes.
Hanna Silenb8dc7fa2021-05-20 17:37:56 +020046constexpr int kMaxResidualGainChange = 15;
pbos@webrtc.org788acd12014-12-15 09:41:24 +000047
48// Maximum additional gain allowed to compensate for microphone level
49// restrictions from clipping events.
Hanna Silenb8dc7fa2021-05-20 17:37:56 +020050constexpr int kSurplusCompressionGain = 6;
pbos@webrtc.org788acd12014-12-15 09:41:24 +000051
Alessio Bazzica42dacda2021-06-17 17:18:46 +020052// History size for the clipping predictor evaluator (unit: number of 10 ms
53// frames).
54constexpr int kClippingPredictorEvaluatorHistorySize = 32;
55
Hanna Silena0047152021-06-03 03:29:38 +020056using ClippingPredictorConfig = AudioProcessing::Config::GainController1::
57 AnalogGainController::ClippingPredictor;
58
Per Åhgren26cc5e62019-11-26 22:58:53 +010059// Returns whether a fall-back solution to choose the maximum level should be
60// chosen.
61bool UseMaxAnalogChannelLevel() {
62 return field_trial::IsEnabled("WebRTC-UseMaxAnalogAgcChannelLevel");
63}
64
henrikaebf45522019-11-04 13:59:21 +010065// Returns kMinMicLevel if no field trial exists or if it has been disabled.
66// Returns a value between 0 and 255 depending on the field-trial string.
67// Example: 'WebRTC-Audio-AgcMinMicLevelExperiment/Enabled-80' => returns 80.
68int GetMinMicLevel() {
69 RTC_LOG(LS_INFO) << "[agc] GetMinMicLevel";
70 constexpr char kMinMicLevelFieldTrial[] =
71 "WebRTC-Audio-AgcMinMicLevelExperiment";
72 if (!webrtc::field_trial::IsEnabled(kMinMicLevelFieldTrial)) {
73 RTC_LOG(LS_INFO) << "[agc] Using default min mic level: " << kMinMicLevel;
74 return kMinMicLevel;
75 }
76 const auto field_trial_string =
77 webrtc::field_trial::FindFullName(kMinMicLevelFieldTrial);
78 int min_mic_level = -1;
79 sscanf(field_trial_string.c_str(), "Enabled-%d", &min_mic_level);
80 if (min_mic_level >= 0 && min_mic_level <= 255) {
81 RTC_LOG(LS_INFO) << "[agc] Experimental min mic level: " << min_mic_level;
82 return min_mic_level;
83 } else {
84 RTC_LOG(LS_WARNING) << "[agc] Invalid parameter for "
85 << kMinMicLevelFieldTrial << ", ignored.";
86 return kMinMicLevel;
87 }
Bjorn Volckeradc46c42015-04-15 11:42:40 +020088}
89
henrikaebf45522019-11-04 13:59:21 +010090int ClampLevel(int mic_level, int min_mic_level) {
91 return rtc::SafeClamp(mic_level, min_mic_level, kMaxMicLevel);
92}
93
94int LevelFromGainError(int gain_error, int level, int min_mic_level) {
kwiberg9e2be5f2016-09-14 05:23:22 -070095 RTC_DCHECK_GE(level, 0);
96 RTC_DCHECK_LE(level, kMaxMicLevel);
pbos@webrtc.org788acd12014-12-15 09:41:24 +000097 if (gain_error == 0) {
98 return level;
99 }
henrikaebf45522019-11-04 13:59:21 +0100100
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000101 int new_level = level;
102 if (gain_error > 0) {
103 while (kGainMap[new_level] - kGainMap[level] < gain_error &&
Yves Gerey665174f2018-06-19 15:03:05 +0200104 new_level < kMaxMicLevel) {
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000105 ++new_level;
106 }
107 } else {
108 while (kGainMap[new_level] - kGainMap[level] > gain_error &&
henrikaebf45522019-11-04 13:59:21 +0100109 new_level > min_mic_level) {
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000110 --new_level;
111 }
112 }
113 return new_level;
114}
115
Per Åhgren361d1c32019-11-06 22:17:14 +0100116// Returns the proportion of samples in the buffer which are at full-scale
117// (and presumably clipped).
118float ComputeClippedRatio(const float* const* audio,
119 size_t num_channels,
120 size_t samples_per_channel) {
Per Åhgrenb49aec52019-11-07 08:41:20 +0100121 RTC_DCHECK_GT(samples_per_channel, 0);
Per Åhgren361d1c32019-11-06 22:17:14 +0100122 int num_clipped = 0;
123 for (size_t ch = 0; ch < num_channels; ++ch) {
Per Åhgrenb49aec52019-11-07 08:41:20 +0100124 int num_clipped_in_ch = 0;
Per Åhgren361d1c32019-11-06 22:17:14 +0100125 for (size_t i = 0; i < samples_per_channel; ++i) {
126 RTC_DCHECK(audio[ch]);
127 if (audio[ch][i] >= 32767.f || audio[ch][i] <= -32768.f) {
Per Åhgrenb49aec52019-11-07 08:41:20 +0100128 ++num_clipped_in_ch;
Per Åhgren361d1c32019-11-06 22:17:14 +0100129 }
130 }
Per Åhgrenb49aec52019-11-07 08:41:20 +0100131 num_clipped = std::max(num_clipped, num_clipped_in_ch);
Per Åhgren361d1c32019-11-06 22:17:14 +0100132 }
Per Åhgrenb49aec52019-11-07 08:41:20 +0100133 return static_cast<float>(num_clipped) / (samples_per_channel);
Per Åhgren361d1c32019-11-06 22:17:14 +0100134}
135
Alessio Bazzica42dacda2021-06-17 17:18:46 +0200136void LogClippingPredictorMetrics(const ClippingPredictorEvaluator& evaluator) {
137 RTC_LOG(LS_INFO) << "Clipping predictor metrics: TP "
138 << evaluator.true_positives() << " TN "
139 << evaluator.true_negatives() << " FP "
140 << evaluator.false_positives() << " FN "
141 << evaluator.false_negatives();
142 const float precision_denominator =
143 evaluator.true_positives() + evaluator.false_positives();
144 const float recall_denominator =
145 evaluator.true_positives() + evaluator.false_negatives();
146 if (precision_denominator > 0 && recall_denominator > 0) {
147 const float precision = evaluator.true_positives() / precision_denominator;
148 const float recall = evaluator.true_positives() / recall_denominator;
149 RTC_LOG(LS_INFO) << "Clipping predictor metrics: P " << precision << " R "
150 << recall;
151 const float f1_score_denominator = precision + recall;
152 if (f1_score_denominator > 0.0f) {
153 const float f1_score = 2 * precision * recall / f1_score_denominator;
154 RTC_LOG(LS_INFO) << "Clipping predictor metrics: F1 " << f1_score;
155 RTC_HISTOGRAM_COUNTS_LINEAR("WebRTC.Audio.Agc.ClippingPredictor.F1Score",
156 std::round(f1_score * 100.0f), /*min=*/0,
157 /*max=*/100,
158 /*bucket_count=*/50);
159 }
160 }
161}
162
Hanna Silene7e92922021-07-08 17:26:31 +0200163void LogClippingMetrics(int clipping_rate) {
164 RTC_LOG(LS_INFO) << "Input clipping rate: " << clipping_rate << "%";
165 RTC_HISTOGRAM_COUNTS_LINEAR("WebRTC.Audio.Agc.InputClippingRate",
166 clipping_rate, /*min=*/0, /*max=*/100,
167 /*bucket_count=*/50);
168}
169
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000170} // namespace
171
Per Åhgren3daedb62019-11-22 12:11:40 +0100172MonoAgc::MonoAgc(ApmDataDumper* data_dumper,
173 int startup_min_level,
174 int clipped_level_min,
Per Åhgren3daedb62019-11-22 12:11:40 +0100175 bool disable_digital_adaptive,
176 int min_mic_level)
177 : min_mic_level_(min_mic_level),
178 disable_digital_adaptive_(disable_digital_adaptive),
Alessio Bazzica42eef862021-01-15 16:41:48 +0100179 agc_(std::make_unique<Agc>()),
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000180 max_level_(kMaxMicLevel),
181 max_compression_gain_(kMaxCompressionGain),
182 target_compression_(kDefaultCompressionGain),
183 compression_(target_compression_),
184 compression_accumulator_(compression_),
henrikaebf45522019-11-04 13:59:21 +0100185 startup_min_level_(ClampLevel(startup_min_level, min_mic_level_)),
Alessio Bazzica42eef862021-01-15 16:41:48 +0100186 clipped_level_min_(clipped_level_min) {}
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000187
Per Åhgren3daedb62019-11-22 12:11:40 +0100188MonoAgc::~MonoAgc() = default;
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000189
Per Åhgren3daedb62019-11-22 12:11:40 +0100190void MonoAgc::Initialize() {
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000191 max_level_ = kMaxMicLevel;
192 max_compression_gain_ = kMaxCompressionGain;
Alex Loiko9489c3a2018-08-09 15:04:24 +0200193 target_compression_ = disable_digital_adaptive_ ? 0 : kDefaultCompressionGain;
194 compression_ = disable_digital_adaptive_ ? 0 : target_compression_;
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000195 compression_accumulator_ = compression_;
Per Åhgren0a144a72021-02-09 08:47:51 +0100196 capture_output_used_ = true;
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000197 check_volume_on_next_process_ = true;
Per Åhgren0e3198e2019-11-18 08:52:22 +0100198}
Alex Loikoc1676732018-07-02 12:05:28 +0200199
Per Åhgren3daedb62019-11-22 12:11:40 +0100200void MonoAgc::Process(const int16_t* audio,
201 size_t samples_per_channel,
202 int sample_rate_hz) {
203 new_compression_to_set_ = absl::nullopt;
Per Åhgren928146f2019-08-20 09:19:21 +0200204
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000205 if (check_volume_on_next_process_) {
206 check_volume_on_next_process_ = false;
207 // We have to wait until the first process call to check the volume,
208 // because Chromium doesn't guarantee it to be valid any earlier.
209 CheckVolumeAndReset();
210 }
211
Per Åhgren3daedb62019-11-22 12:11:40 +0100212 agc_->Process(audio, samples_per_channel, sample_rate_hz);
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000213
214 UpdateGain();
Alex Loiko9489c3a2018-08-09 15:04:24 +0200215 if (!disable_digital_adaptive_) {
216 UpdateCompressor();
217 }
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000218}
219
Hanna Silenb8dc7fa2021-05-20 17:37:56 +0200220void MonoAgc::HandleClipping(int clipped_level_step) {
Per Åhgren3daedb62019-11-22 12:11:40 +0100221 // Always decrease the maximum level, even if the current level is below
222 // threshold.
Hanna Silenb8dc7fa2021-05-20 17:37:56 +0200223 SetMaxLevel(std::max(clipped_level_min_, max_level_ - clipped_level_step));
Per Åhgren3daedb62019-11-22 12:11:40 +0100224 if (log_to_histograms_) {
225 RTC_HISTOGRAM_BOOLEAN("WebRTC.Audio.AgcClippingAdjustmentAllowed",
Hanna Silenb8dc7fa2021-05-20 17:37:56 +0200226 level_ - clipped_level_step >= clipped_level_min_);
Per Åhgren3daedb62019-11-22 12:11:40 +0100227 }
228 if (level_ > clipped_level_min_) {
229 // Don't try to adjust the level if we're already below the limit. As
230 // a consequence, if the user has brought the level above the limit, we
231 // will still not react until the postproc updates the level.
Hanna Silenb8dc7fa2021-05-20 17:37:56 +0200232 SetLevel(std::max(clipped_level_min_, level_ - clipped_level_step));
Per Åhgren3daedb62019-11-22 12:11:40 +0100233 // Reset the AGCs for all channels since the level has changed.
234 agc_->Reset();
235 }
236}
237
238void MonoAgc::SetLevel(int new_level) {
Per Åhgren0e3198e2019-11-18 08:52:22 +0100239 int voe_level = stream_analog_level_;
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000240 if (voe_level == 0) {
Jonas Olsson645b0272018-02-15 15:16:27 +0100241 RTC_DLOG(LS_INFO)
Mirko Bonadei675513b2017-11-09 11:09:25 +0100242 << "[agc] VolumeCallbacks returned level=0, taking no action.";
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000243 return;
244 }
Jonas Olsson645b0272018-02-15 15:16:27 +0100245 if (voe_level < 0 || voe_level > kMaxMicLevel) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100246 RTC_LOG(LS_ERROR) << "VolumeCallbacks returned an invalid level="
247 << voe_level;
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000248 return;
249 }
250
251 if (voe_level > level_ + kLevelQuantizationSlack ||
252 voe_level < level_ - kLevelQuantizationSlack) {
Jonas Olsson645b0272018-02-15 15:16:27 +0100253 RTC_DLOG(LS_INFO) << "[agc] Mic volume was manually adjusted. Updating "
Yves Gerey665174f2018-06-19 15:03:05 +0200254 "stored level from "
255 << level_ << " to " << voe_level;
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000256 level_ = voe_level;
257 // Always allow the user to increase the volume.
258 if (level_ > max_level_) {
259 SetMaxLevel(level_);
260 }
261 // Take no action in this case, since we can't be sure when the volume
262 // was manually adjusted. The compressor will still provide some of the
263 // desired gain change.
264 agc_->Reset();
Per Åhgren3daedb62019-11-22 12:11:40 +0100265
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000266 return;
267 }
268
269 new_level = std::min(new_level, max_level_);
270 if (new_level == level_) {
271 return;
272 }
273
Per Åhgren0e3198e2019-11-18 08:52:22 +0100274 stream_analog_level_ = new_level;
Jonas Olsson6c9bc392020-01-14 15:54:35 +0100275 RTC_DLOG(LS_INFO) << "[agc] voe_level=" << voe_level << ", level_=" << level_
276 << ", new_level=" << new_level;
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000277 level_ = new_level;
278}
279
Per Åhgren3daedb62019-11-22 12:11:40 +0100280void MonoAgc::SetMaxLevel(int level) {
henrik.lundinbd681b92016-12-05 09:08:42 -0800281 RTC_DCHECK_GE(level, clipped_level_min_);
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000282 max_level_ = level;
283 // Scale the |kSurplusCompressionGain| linearly across the restricted
284 // level range.
henrik.lundinbd681b92016-12-05 09:08:42 -0800285 max_compression_gain_ =
286 kMaxCompressionGain + std::floor((1.f * kMaxMicLevel - max_level_) /
287 (kMaxMicLevel - clipped_level_min_) *
288 kSurplusCompressionGain +
289 0.5f);
Jonas Olsson645b0272018-02-15 15:16:27 +0100290 RTC_DLOG(LS_INFO) << "[agc] max_level_=" << max_level_
291 << ", max_compression_gain_=" << max_compression_gain_;
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000292}
293
Per Åhgren0a144a72021-02-09 08:47:51 +0100294void MonoAgc::HandleCaptureOutputUsedChange(bool capture_output_used) {
295 if (capture_output_used_ == capture_output_used) {
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000296 return;
297 }
Per Åhgren0a144a72021-02-09 08:47:51 +0100298 capture_output_used_ = capture_output_used;
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000299
Per Åhgren0a144a72021-02-09 08:47:51 +0100300 if (capture_output_used) {
301 // When we start using the output, we should reset things to be safe.
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000302 check_volume_on_next_process_ = true;
303 }
304}
305
Per Åhgren3daedb62019-11-22 12:11:40 +0100306int MonoAgc::CheckVolumeAndReset() {
Per Åhgren0e3198e2019-11-18 08:52:22 +0100307 int level = stream_analog_level_;
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000308 // Reasons for taking action at startup:
309 // 1) A person starting a call is expected to be heard.
310 // 2) Independent of interpretation of |level| == 0 we should raise it so the
311 // AGC can do its job properly.
312 if (level == 0 && !startup_) {
Jonas Olsson645b0272018-02-15 15:16:27 +0100313 RTC_DLOG(LS_INFO)
Mirko Bonadei675513b2017-11-09 11:09:25 +0100314 << "[agc] VolumeCallbacks returned level=0, taking no action.";
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000315 return 0;
316 }
Jonas Olsson645b0272018-02-15 15:16:27 +0100317 if (level < 0 || level > kMaxMicLevel) {
318 RTC_LOG(LS_ERROR) << "[agc] VolumeCallbacks returned an invalid level="
319 << level;
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000320 return -1;
321 }
Jonas Olsson645b0272018-02-15 15:16:27 +0100322 RTC_DLOG(LS_INFO) << "[agc] Initial GetMicVolume()=" << level;
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000323
henrikaebf45522019-11-04 13:59:21 +0100324 int minLevel = startup_ ? startup_min_level_ : min_mic_level_;
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000325 if (level < minLevel) {
326 level = minLevel;
Jonas Olsson645b0272018-02-15 15:16:27 +0100327 RTC_DLOG(LS_INFO) << "[agc] Initial volume too low, raising to " << level;
Per Åhgren0e3198e2019-11-18 08:52:22 +0100328 stream_analog_level_ = level;
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000329 }
330 agc_->Reset();
331 level_ = level;
332 startup_ = false;
333 return 0;
334}
335
336// Requests the RMS error from AGC and distributes the required gain change
337// between the digital compression stage and volume slider. We use the
338// compressor first, providing a slack region around the current slider
339// position to reduce movement.
340//
341// If the slider needs to be moved, we check first if the user has adjusted
342// it, in which case we take no action and cache the updated level.
Per Åhgren3daedb62019-11-22 12:11:40 +0100343void MonoAgc::UpdateGain() {
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000344 int rms_error = 0;
345 if (!agc_->GetRmsErrorDb(&rms_error)) {
346 // No error update ready.
347 return;
348 }
349 // The compressor will always add at least kMinCompressionGain. In effect,
350 // this adjusts our target gain upward by the same amount and rms_error
351 // needs to reflect that.
352 rms_error += kMinCompressionGain;
353
354 // Handle as much error as possible with the compressor first.
kwiberg07038562017-06-12 11:40:47 -0700355 int raw_compression =
356 rtc::SafeClamp(rms_error, kMinCompressionGain, max_compression_gain_);
357
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000358 // Deemphasize the compression gain error. Move halfway between the current
359 // target and the newly received target. This serves to soften perceptible
360 // intra-talkspurt adjustments, at the cost of some adaptation speed.
361 if ((raw_compression == max_compression_gain_ &&
Yves Gerey665174f2018-06-19 15:03:05 +0200362 target_compression_ == max_compression_gain_ - 1) ||
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000363 (raw_compression == kMinCompressionGain &&
Yves Gerey665174f2018-06-19 15:03:05 +0200364 target_compression_ == kMinCompressionGain + 1)) {
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000365 // Special case to allow the target to reach the endpoints of the
366 // compression range. The deemphasis would otherwise halt it at 1 dB shy.
367 target_compression_ = raw_compression;
368 } else {
Yves Gerey665174f2018-06-19 15:03:05 +0200369 target_compression_ =
370 (raw_compression - target_compression_) / 2 + target_compression_;
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000371 }
372
373 // Residual error will be handled by adjusting the volume slider. Use the
374 // raw rather than deemphasized compression here as we would otherwise
375 // shrink the amount of slack the compressor provides.
kwiberg07038562017-06-12 11:40:47 -0700376 const int residual_gain =
377 rtc::SafeClamp(rms_error - raw_compression, -kMaxResidualGainChange,
378 kMaxResidualGainChange);
Jonas Olsson645b0272018-02-15 15:16:27 +0100379 RTC_DLOG(LS_INFO) << "[agc] rms_error=" << rms_error
380 << ", target_compression=" << target_compression_
381 << ", residual_gain=" << residual_gain;
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000382 if (residual_gain == 0)
383 return;
384
henrik.lundin3edc7f02016-11-24 01:42:46 -0800385 int old_level = level_;
henrikaebf45522019-11-04 13:59:21 +0100386 SetLevel(LevelFromGainError(residual_gain, level_, min_mic_level_));
henrik.lundin3edc7f02016-11-24 01:42:46 -0800387 if (old_level != level_) {
388 // level_ was updated by SetLevel; log the new value.
henrik.lundin45bb5132016-12-06 04:28:04 -0800389 RTC_HISTOGRAM_COUNTS_LINEAR("WebRTC.Audio.AgcSetLevel", level_, 1,
390 kMaxMicLevel, 50);
Alex Loiko99f1e0d2018-07-19 16:39:39 +0200391 // Reset the AGC since the level has changed.
392 agc_->Reset();
henrik.lundin3edc7f02016-11-24 01:42:46 -0800393 }
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000394}
395
Per Åhgren3daedb62019-11-22 12:11:40 +0100396void MonoAgc::UpdateCompressor() {
Alex Loikof3122e02018-08-10 14:43:51 +0200397 calls_since_last_gain_log_++;
398 if (calls_since_last_gain_log_ == 100) {
399 calls_since_last_gain_log_ = 0;
400 RTC_HISTOGRAM_COUNTS_LINEAR("WebRTC.Audio.Agc.DigitalGainApplied",
401 compression_, 0, kMaxCompressionGain,
402 kMaxCompressionGain + 1);
403 }
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000404 if (compression_ == target_compression_) {
405 return;
406 }
407
408 // Adapt the compression gain slowly towards the target, in order to avoid
409 // highly perceptible changes.
410 if (target_compression_ > compression_) {
411 compression_accumulator_ += kCompressionGainStep;
412 } else {
413 compression_accumulator_ -= kCompressionGainStep;
414 }
415
416 // The compressor accepts integer gains in dB. Adjust the gain when
417 // we've come within half a stepsize of the nearest integer. (We don't
418 // check for equality due to potential floating point imprecision).
419 int new_compression = compression_;
420 int nearest_neighbor = std::floor(compression_accumulator_ + 0.5);
421 if (std::fabs(compression_accumulator_ - nearest_neighbor) <
422 kCompressionGainStep / 2) {
423 new_compression = nearest_neighbor;
424 }
425
426 // Set the new compression gain.
427 if (new_compression != compression_) {
Alex Loikof3122e02018-08-10 14:43:51 +0200428 RTC_HISTOGRAM_COUNTS_LINEAR("WebRTC.Audio.Agc.DigitalGainUpdated",
429 new_compression, 0, kMaxCompressionGain,
430 kMaxCompressionGain + 1);
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000431 compression_ = new_compression;
432 compression_accumulator_ = new_compression;
Per Åhgren0e3198e2019-11-18 08:52:22 +0100433 new_compression_to_set_ = compression_;
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000434 }
435}
436
Per Åhgren3daedb62019-11-22 12:11:40 +0100437int AgcManagerDirect::instance_counter_ = 0;
438
Alessio Bazzica42dacda2021-06-17 17:18:46 +0200439AgcManagerDirect::AgcManagerDirect(
440 Agc* agc,
441 int startup_min_level,
442 int clipped_level_min,
443 int sample_rate_hz,
444 int clipped_level_step,
445 float clipped_ratio_threshold,
446 int clipped_wait_frames,
447 const ClippingPredictorConfig& clipping_config)
Per Åhgren3daedb62019-11-22 12:11:40 +0100448 : AgcManagerDirect(/*num_capture_channels*/ 1,
449 startup_min_level,
450 clipped_level_min,
Per Åhgren3daedb62019-11-22 12:11:40 +0100451 /*disable_digital_adaptive*/ false,
Hanna Silenb8dc7fa2021-05-20 17:37:56 +0200452 sample_rate_hz,
453 clipped_level_step,
454 clipped_ratio_threshold,
Hanna Silena0047152021-06-03 03:29:38 +0200455 clipped_wait_frames,
Alessio Bazzica42dacda2021-06-17 17:18:46 +0200456 clipping_config) {
Per Åhgren3daedb62019-11-22 12:11:40 +0100457 RTC_DCHECK(channel_agcs_[0]);
458 RTC_DCHECK(agc);
459 channel_agcs_[0]->set_agc(agc);
460}
461
Alessio Bazzica42dacda2021-06-17 17:18:46 +0200462AgcManagerDirect::AgcManagerDirect(
463 int num_capture_channels,
464 int startup_min_level,
465 int clipped_level_min,
466 bool disable_digital_adaptive,
467 int sample_rate_hz,
468 int clipped_level_step,
469 float clipped_ratio_threshold,
470 int clipped_wait_frames,
471 const ClippingPredictorConfig& clipping_config)
Per Åhgren3daedb62019-11-22 12:11:40 +0100472 : data_dumper_(
473 new ApmDataDumper(rtc::AtomicOps::Increment(&instance_counter_))),
Per Åhgren26cc5e62019-11-26 22:58:53 +0100474 use_min_channel_level_(!UseMaxAnalogChannelLevel()),
Per Åhgren3daedb62019-11-22 12:11:40 +0100475 sample_rate_hz_(sample_rate_hz),
476 num_capture_channels_(num_capture_channels),
477 disable_digital_adaptive_(disable_digital_adaptive),
Hanna Silenb8dc7fa2021-05-20 17:37:56 +0200478 frames_since_clipped_(clipped_wait_frames),
Per Åhgren0a144a72021-02-09 08:47:51 +0100479 capture_output_used_(true),
Hanna Silenb8dc7fa2021-05-20 17:37:56 +0200480 clipped_level_step_(clipped_level_step),
481 clipped_ratio_threshold_(clipped_ratio_threshold),
482 clipped_wait_frames_(clipped_wait_frames),
Per Åhgren3daedb62019-11-22 12:11:40 +0100483 channel_agcs_(num_capture_channels),
Hanna Silena0047152021-06-03 03:29:38 +0200484 new_compressions_to_set_(num_capture_channels),
485 clipping_predictor_(
Alessio Bazzica42dacda2021-06-17 17:18:46 +0200486 CreateClippingPredictor(num_capture_channels, clipping_config)),
487 use_clipping_predictor_step_(!!clipping_predictor_ &&
488 clipping_config.use_predicted_step),
489 clipping_predictor_evaluator_(kClippingPredictorEvaluatorHistorySize),
Hanna Silene7e92922021-07-08 17:26:31 +0200490 clipping_predictor_log_counter_(0),
491 clipping_rate_log_(0.0f),
492 clipping_rate_log_counter_(0) {
Per Åhgren3daedb62019-11-22 12:11:40 +0100493 const int min_mic_level = GetMinMicLevel();
494 for (size_t ch = 0; ch < channel_agcs_.size(); ++ch) {
495 ApmDataDumper* data_dumper_ch = ch == 0 ? data_dumper_.get() : nullptr;
496
497 channel_agcs_[ch] = std::make_unique<MonoAgc>(
498 data_dumper_ch, startup_min_level, clipped_level_min,
Alessio Bazzica42eef862021-01-15 16:41:48 +0100499 disable_digital_adaptive_, min_mic_level);
Per Åhgren3daedb62019-11-22 12:11:40 +0100500 }
Hanna Silenb8dc7fa2021-05-20 17:37:56 +0200501 RTC_DCHECK(!channel_agcs_.empty());
502 RTC_DCHECK_GT(clipped_level_step, 0);
503 RTC_DCHECK_LE(clipped_level_step, 255);
504 RTC_DCHECK_GT(clipped_ratio_threshold, 0.f);
505 RTC_DCHECK_LT(clipped_ratio_threshold, 1.f);
506 RTC_DCHECK_GT(clipped_wait_frames, 0);
Per Åhgren3daedb62019-11-22 12:11:40 +0100507 channel_agcs_[0]->ActivateLogging();
508}
509
510AgcManagerDirect::~AgcManagerDirect() {}
511
512void AgcManagerDirect::Initialize() {
513 RTC_DLOG(LS_INFO) << "AgcManagerDirect::Initialize";
514 data_dumper_->InitiateNewSetOfRecordings();
515 for (size_t ch = 0; ch < channel_agcs_.size(); ++ch) {
516 channel_agcs_[ch]->Initialize();
517 }
Per Åhgren0a144a72021-02-09 08:47:51 +0100518 capture_output_used_ = true;
Per Åhgren3daedb62019-11-22 12:11:40 +0100519
520 AggregateChannelLevels();
Alessio Bazzica42dacda2021-06-17 17:18:46 +0200521 clipping_predictor_evaluator_.Reset();
522 clipping_predictor_log_counter_ = 0;
Hanna Silene7e92922021-07-08 17:26:31 +0200523 clipping_rate_log_ = 0.0f;
524 clipping_rate_log_counter_ = 0;
Per Åhgren3daedb62019-11-22 12:11:40 +0100525}
526
527void AgcManagerDirect::SetupDigitalGainControl(
528 GainControl* gain_control) const {
529 RTC_DCHECK(gain_control);
530 if (gain_control->set_mode(GainControl::kFixedDigital) != 0) {
531 RTC_LOG(LS_ERROR) << "set_mode(GainControl::kFixedDigital) failed.";
532 }
533 const int target_level_dbfs = disable_digital_adaptive_ ? 0 : 2;
534 if (gain_control->set_target_level_dbfs(target_level_dbfs) != 0) {
535 RTC_LOG(LS_ERROR) << "set_target_level_dbfs() failed.";
536 }
537 const int compression_gain_db =
538 disable_digital_adaptive_ ? 0 : kDefaultCompressionGain;
539 if (gain_control->set_compression_gain_db(compression_gain_db) != 0) {
540 RTC_LOG(LS_ERROR) << "set_compression_gain_db() failed.";
541 }
542 const bool enable_limiter = !disable_digital_adaptive_;
543 if (gain_control->enable_limiter(enable_limiter) != 0) {
544 RTC_LOG(LS_ERROR) << "enable_limiter() failed.";
545 }
546}
547
548void AgcManagerDirect::AnalyzePreProcess(const AudioBuffer* audio) {
549 RTC_DCHECK(audio);
550 AnalyzePreProcess(audio->channels_const(), audio->num_frames());
551}
552
553void AgcManagerDirect::AnalyzePreProcess(const float* const* audio,
554 size_t samples_per_channel) {
555 RTC_DCHECK(audio);
556 AggregateChannelLevels();
Per Åhgren0a144a72021-02-09 08:47:51 +0100557 if (!capture_output_used_) {
Per Åhgren3daedb62019-11-22 12:11:40 +0100558 return;
559 }
560
Hanna Silena0047152021-06-03 03:29:38 +0200561 if (!!clipping_predictor_) {
562 AudioFrameView<const float> frame = AudioFrameView<const float>(
563 audio, num_capture_channels_, static_cast<int>(samples_per_channel));
Alessio Bazzicab237a872021-06-11 12:37:54 +0200564 clipping_predictor_->Analyze(frame);
Hanna Silena0047152021-06-03 03:29:38 +0200565 }
566
Per Åhgren3daedb62019-11-22 12:11:40 +0100567 // Check for clipped samples, as the AGC has difficulty detecting pitch
568 // under clipping distortion. We do this in the preprocessing phase in order
569 // to catch clipped echo as well.
570 //
571 // If we find a sufficiently clipped frame, drop the current microphone level
572 // and enforce a new maximum level, dropped the same amount from the current
573 // maximum. This harsh treatment is an effort to avoid repeated clipped echo
574 // events. As compensation for this restriction, the maximum compression
575 // gain is increased, through SetMaxLevel().
576 float clipped_ratio =
577 ComputeClippedRatio(audio, num_capture_channels_, samples_per_channel);
Hanna Silene7e92922021-07-08 17:26:31 +0200578 clipping_rate_log_ = std::max(clipped_ratio, clipping_rate_log_);
579 clipping_rate_log_counter_++;
580 constexpr int kNumFramesIn30Seconds = 3000;
581 if (clipping_rate_log_counter_ == kNumFramesIn30Seconds) {
582 LogClippingMetrics(std::round(100.0f * clipping_rate_log_));
583 clipping_rate_log_ = 0.0f;
584 clipping_rate_log_counter_ = 0;
585 }
586
587 if (frames_since_clipped_ < clipped_wait_frames_) {
588 ++frames_since_clipped_;
589 return;
590 }
591
Hanna Silena0047152021-06-03 03:29:38 +0200592 const bool clipping_detected = clipped_ratio > clipped_ratio_threshold_;
593 bool clipping_predicted = false;
594 int predicted_step = 0;
595 if (!!clipping_predictor_) {
596 for (int channel = 0; channel < num_capture_channels_; ++channel) {
597 const auto step = clipping_predictor_->EstimateClippedLevelStep(
598 channel, stream_analog_level_, clipped_level_step_,
599 channel_agcs_[channel]->min_mic_level(), kMaxMicLevel);
Alessio Bazzica42dacda2021-06-17 17:18:46 +0200600 if (use_clipping_predictor_step_ && step.has_value()) {
Hanna Silena0047152021-06-03 03:29:38 +0200601 predicted_step = std::max(predicted_step, step.value());
602 clipping_predicted = true;
603 }
604 }
Alessio Bazzica42dacda2021-06-17 17:18:46 +0200605 // Clipping prediction evaluation.
606 absl::optional<int> prediction_interval =
607 clipping_predictor_evaluator_.Observe(clipping_detected,
608 clipping_predicted);
609 if (prediction_interval.has_value()) {
610 RTC_HISTOGRAM_COUNTS_LINEAR(
611 "WebRTC.Audio.Agc.ClippingPredictor.PredictionInterval",
612 prediction_interval.value(), /*min=*/0,
613 /*max=*/49, /*bucket_count=*/50);
614 }
Alessio Bazzica42dacda2021-06-17 17:18:46 +0200615 clipping_predictor_log_counter_++;
616 if (clipping_predictor_log_counter_ == kNumFramesIn30Seconds) {
617 LogClippingPredictorMetrics(clipping_predictor_evaluator_);
618 clipping_predictor_log_counter_ = 0;
619 }
Hanna Silena0047152021-06-03 03:29:38 +0200620 }
621 if (clipping_detected || clipping_predicted) {
622 int step = clipped_level_step_;
623 if (clipping_detected) {
624 RTC_DLOG(LS_INFO) << "[agc] Clipping detected. clipped_ratio="
625 << clipped_ratio;
626 }
627 if (clipping_predicted) {
628 step = std::max(predicted_step, clipped_level_step_);
629 RTC_DLOG(LS_INFO) << "[agc] Clipping predicted. step=" << step;
630 }
Per Åhgren3daedb62019-11-22 12:11:40 +0100631 for (auto& state_ch : channel_agcs_) {
Hanna Silena0047152021-06-03 03:29:38 +0200632 state_ch->HandleClipping(step);
Per Åhgren3daedb62019-11-22 12:11:40 +0100633 }
634 frames_since_clipped_ = 0;
Hanna Silena0047152021-06-03 03:29:38 +0200635 if (!!clipping_predictor_) {
636 clipping_predictor_->Reset();
Alessio Bazzica42dacda2021-06-17 17:18:46 +0200637 clipping_predictor_evaluator_.Reset();
Hanna Silena0047152021-06-03 03:29:38 +0200638 }
Per Åhgren3daedb62019-11-22 12:11:40 +0100639 }
640 AggregateChannelLevels();
641}
642
643void AgcManagerDirect::Process(const AudioBuffer* audio) {
644 AggregateChannelLevels();
645
Per Åhgren0a144a72021-02-09 08:47:51 +0100646 if (!capture_output_used_) {
Per Åhgren3daedb62019-11-22 12:11:40 +0100647 return;
648 }
649
650 for (size_t ch = 0; ch < channel_agcs_.size(); ++ch) {
651 int16_t* audio_use = nullptr;
652 std::array<int16_t, AudioBuffer::kMaxSampleRate / 100> audio_data;
653 int num_frames_per_band;
654 if (audio) {
655 FloatS16ToS16(audio->split_bands_const_f(ch)[0],
656 audio->num_frames_per_band(), audio_data.data());
657 audio_use = audio_data.data();
658 num_frames_per_band = audio->num_frames_per_band();
659 } else {
660 // Only used for testing.
661 // TODO(peah): Change unittests to only allow on non-null audio input.
662 num_frames_per_band = 320;
663 }
664 channel_agcs_[ch]->Process(audio_use, num_frames_per_band, sample_rate_hz_);
665 new_compressions_to_set_[ch] = channel_agcs_[ch]->new_compression();
666 }
667
668 AggregateChannelLevels();
669}
670
671absl::optional<int> AgcManagerDirect::GetDigitalComressionGain() {
672 return new_compressions_to_set_[channel_controlling_gain_];
673}
674
Per Åhgren0a144a72021-02-09 08:47:51 +0100675void AgcManagerDirect::HandleCaptureOutputUsedChange(bool capture_output_used) {
Per Åhgren3daedb62019-11-22 12:11:40 +0100676 for (size_t ch = 0; ch < channel_agcs_.size(); ++ch) {
Per Åhgren0a144a72021-02-09 08:47:51 +0100677 channel_agcs_[ch]->HandleCaptureOutputUsedChange(capture_output_used);
Per Åhgren3daedb62019-11-22 12:11:40 +0100678 }
Per Åhgren0a144a72021-02-09 08:47:51 +0100679 capture_output_used_ = capture_output_used;
Per Åhgren3daedb62019-11-22 12:11:40 +0100680}
681
682float AgcManagerDirect::voice_probability() const {
683 float max_prob = 0.f;
684 for (const auto& state_ch : channel_agcs_) {
685 max_prob = std::max(max_prob, state_ch->voice_probability());
686 }
687
688 return max_prob;
689}
690
691void AgcManagerDirect::set_stream_analog_level(int level) {
692 for (size_t ch = 0; ch < channel_agcs_.size(); ++ch) {
693 channel_agcs_[ch]->set_stream_analog_level(level);
694 }
695
696 AggregateChannelLevels();
697}
698
699void AgcManagerDirect::AggregateChannelLevels() {
700 stream_analog_level_ = channel_agcs_[0]->stream_analog_level();
701 channel_controlling_gain_ = 0;
Per Åhgren26cc5e62019-11-26 22:58:53 +0100702 if (use_min_channel_level_) {
703 for (size_t ch = 1; ch < channel_agcs_.size(); ++ch) {
704 int level = channel_agcs_[ch]->stream_analog_level();
705 if (level < stream_analog_level_) {
706 stream_analog_level_ = level;
707 channel_controlling_gain_ = static_cast<int>(ch);
708 }
709 }
710 } else {
711 for (size_t ch = 1; ch < channel_agcs_.size(); ++ch) {
712 int level = channel_agcs_[ch]->stream_analog_level();
713 if (level > stream_analog_level_) {
714 stream_analog_level_ = level;
715 channel_controlling_gain_ = static_cast<int>(ch);
716 }
Per Åhgren3daedb62019-11-22 12:11:40 +0100717 }
718 }
719}
720
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000721} // namespace webrtc