blob: 13b902f17f2256f5285d8424fdc97688c3914c58 [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
16#ifdef WEBRTC_AGC_DEBUG_DUMP
17#include <cstdio>
18#endif
19
Per Åhgren928146f2019-08-20 09:19:21 +020020#include "common_audio/include/audio_util.h"
Sam Zackrisson41478c72019-10-15 10:10:26 +020021#include "modules/audio_processing/agc/gain_control.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020022#include "modules/audio_processing/agc/gain_map_internal.h"
Alex Loiko2ffafa82018-07-06 15:35:42 +020023#include "modules/audio_processing/agc2/adaptive_mode_level_estimator_agc.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020024#include "rtc_base/checks.h"
25#include "rtc_base/logging.h"
Karl Wiberge40468b2017-11-22 10:42:26 +010026#include "rtc_base/numerics/safe_minmax.h"
henrikaebf45522019-11-04 13:59:21 +010027#include "system_wrappers/include/field_trial.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020028#include "system_wrappers/include/metrics.h"
pbos@webrtc.org788acd12014-12-15 09:41:24 +000029
30namespace webrtc {
31
Alex Loikoc1676732018-07-02 12:05:28 +020032int AgcManagerDirect::instance_counter_ = 0;
33
pbos@webrtc.org788acd12014-12-15 09:41:24 +000034namespace {
35
pbos@webrtc.org788acd12014-12-15 09:41:24 +000036// Amount the microphone level is lowered with every clipping event.
37const int kClippedLevelStep = 15;
38// Proportion of clipped samples required to declare a clipping event.
39const float kClippedRatioThreshold = 0.1f;
40// Time in frames to wait after a clipping event before checking again.
41const int kClippedWaitFrames = 300;
42
43// Amount of error we tolerate in the microphone level (presumably due to OS
44// quantization) before we assume the user has manually adjusted the microphone.
45const int kLevelQuantizationSlack = 25;
46
47const int kDefaultCompressionGain = 7;
48const int kMaxCompressionGain = 12;
49const int kMinCompressionGain = 2;
50// Controls the rate of compression changes towards the target.
51const float kCompressionGainStep = 0.05f;
52
53const int kMaxMicLevel = 255;
kwiberg@webrtc.org2ebfac52015-01-14 10:51:54 +000054static_assert(kGainMapSize > kMaxMicLevel, "gain map too small");
pbos@webrtc.org788acd12014-12-15 09:41:24 +000055const int kMinMicLevel = 12;
pbos@webrtc.org788acd12014-12-15 09:41:24 +000056
57// Prevent very large microphone level changes.
58const int kMaxResidualGainChange = 15;
59
60// Maximum additional gain allowed to compensate for microphone level
61// restrictions from clipping events.
62const int kSurplusCompressionGain = 6;
63
Per Åhgren928146f2019-08-20 09:19:21 +020064// Maximum number of channels and number of samples per channel supported.
65constexpr size_t kMaxNumSamplesPerChannel = 1920;
66constexpr size_t kMaxNumChannels = 4;
67
henrikaebf45522019-11-04 13:59:21 +010068// Returns kMinMicLevel if no field trial exists or if it has been disabled.
69// Returns a value between 0 and 255 depending on the field-trial string.
70// Example: 'WebRTC-Audio-AgcMinMicLevelExperiment/Enabled-80' => returns 80.
71int GetMinMicLevel() {
72 RTC_LOG(LS_INFO) << "[agc] GetMinMicLevel";
73 constexpr char kMinMicLevelFieldTrial[] =
74 "WebRTC-Audio-AgcMinMicLevelExperiment";
75 if (!webrtc::field_trial::IsEnabled(kMinMicLevelFieldTrial)) {
76 RTC_LOG(LS_INFO) << "[agc] Using default min mic level: " << kMinMicLevel;
77 return kMinMicLevel;
78 }
79 const auto field_trial_string =
80 webrtc::field_trial::FindFullName(kMinMicLevelFieldTrial);
81 int min_mic_level = -1;
82 sscanf(field_trial_string.c_str(), "Enabled-%d", &min_mic_level);
83 if (min_mic_level >= 0 && min_mic_level <= 255) {
84 RTC_LOG(LS_INFO) << "[agc] Experimental min mic level: " << min_mic_level;
85 return min_mic_level;
86 } else {
87 RTC_LOG(LS_WARNING) << "[agc] Invalid parameter for "
88 << kMinMicLevelFieldTrial << ", ignored.";
89 return kMinMicLevel;
90 }
Bjorn Volckeradc46c42015-04-15 11:42:40 +020091}
92
henrikaebf45522019-11-04 13:59:21 +010093int ClampLevel(int mic_level, int min_mic_level) {
94 return rtc::SafeClamp(mic_level, min_mic_level, kMaxMicLevel);
95}
96
97int LevelFromGainError(int gain_error, int level, int min_mic_level) {
kwiberg9e2be5f2016-09-14 05:23:22 -070098 RTC_DCHECK_GE(level, 0);
99 RTC_DCHECK_LE(level, kMaxMicLevel);
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000100 if (gain_error == 0) {
101 return level;
102 }
henrikaebf45522019-11-04 13:59:21 +0100103
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000104 int new_level = level;
105 if (gain_error > 0) {
106 while (kGainMap[new_level] - kGainMap[level] < gain_error &&
Yves Gerey665174f2018-06-19 15:03:05 +0200107 new_level < kMaxMicLevel) {
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000108 ++new_level;
109 }
110 } else {
111 while (kGainMap[new_level] - kGainMap[level] > gain_error &&
henrikaebf45522019-11-04 13:59:21 +0100112 new_level > min_mic_level) {
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000113 --new_level;
114 }
115 }
116 return new_level;
117}
118
Alex Loiko9489c3a2018-08-09 15:04:24 +0200119int InitializeGainControl(GainControl* gain_control,
120 bool disable_digital_adaptive) {
121 if (gain_control->set_mode(GainControl::kFixedDigital) != 0) {
122 RTC_LOG(LS_ERROR) << "set_mode(GainControl::kFixedDigital) failed.";
123 return -1;
124 }
125 const int target_level_dbfs = disable_digital_adaptive ? 0 : 2;
126 if (gain_control->set_target_level_dbfs(target_level_dbfs) != 0) {
127 RTC_LOG(LS_ERROR) << "set_target_level_dbfs() failed.";
128 return -1;
129 }
130 const int compression_gain_db =
131 disable_digital_adaptive ? 0 : kDefaultCompressionGain;
132 if (gain_control->set_compression_gain_db(compression_gain_db) != 0) {
133 RTC_LOG(LS_ERROR) << "set_compression_gain_db() failed.";
134 return -1;
135 }
136 const bool enable_limiter = !disable_digital_adaptive;
137 if (gain_control->enable_limiter(enable_limiter) != 0) {
138 RTC_LOG(LS_ERROR) << "enable_limiter() failed.";
139 return -1;
140 }
141 return 0;
142}
143
Per Åhgren361d1c32019-11-06 22:17:14 +0100144// Returns the proportion of samples in the buffer which are at full-scale
145// (and presumably clipped).
146float ComputeClippedRatio(const float* const* audio,
147 size_t num_channels,
148 size_t samples_per_channel) {
Per Åhgrenb49aec52019-11-07 08:41:20 +0100149 RTC_DCHECK_GT(samples_per_channel, 0);
Per Åhgren361d1c32019-11-06 22:17:14 +0100150 int num_clipped = 0;
151 for (size_t ch = 0; ch < num_channels; ++ch) {
Per Åhgrenb49aec52019-11-07 08:41:20 +0100152 int num_clipped_in_ch = 0;
Per Åhgren361d1c32019-11-06 22:17:14 +0100153 for (size_t i = 0; i < samples_per_channel; ++i) {
154 RTC_DCHECK(audio[ch]);
155 if (audio[ch][i] >= 32767.f || audio[ch][i] <= -32768.f) {
Per Åhgrenb49aec52019-11-07 08:41:20 +0100156 ++num_clipped_in_ch;
Per Åhgren361d1c32019-11-06 22:17:14 +0100157 }
158 }
Per Åhgrenb49aec52019-11-07 08:41:20 +0100159 num_clipped = std::max(num_clipped, num_clipped_in_ch);
Per Åhgren361d1c32019-11-06 22:17:14 +0100160 }
Per Åhgrenb49aec52019-11-07 08:41:20 +0100161 return static_cast<float>(num_clipped) / (samples_per_channel);
Per Åhgren361d1c32019-11-06 22:17:14 +0100162}
163
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000164} // namespace
165
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000166AgcManagerDirect::AgcManagerDirect(Agc* agc,
167 GainControl* gctrl,
Bjorn Volckeradc46c42015-04-15 11:42:40 +0200168 VolumeCallbacks* volume_callbacks,
henrik.lundinbd681b92016-12-05 09:08:42 -0800169 int startup_min_level,
170 int clipped_level_min)
Per Åhgrenb8c1be52019-11-07 20:35:50 +0100171 : AgcManagerDirect(gctrl,
Alex Loiko64cb83b2018-07-02 13:38:19 +0200172 volume_callbacks,
173 startup_min_level,
174 clipped_level_min,
175 false,
Alex Loiko99f1e0d2018-07-19 16:39:39 +0200176 false) {
177 RTC_DCHECK(agc_);
Per Åhgrenb8c1be52019-11-07 20:35:50 +0100178 agc_.reset(agc);
Alex Loiko99f1e0d2018-07-19 16:39:39 +0200179}
Alex Loiko64cb83b2018-07-02 13:38:19 +0200180
Per Åhgrenb8c1be52019-11-07 20:35:50 +0100181AgcManagerDirect::AgcManagerDirect(GainControl* gctrl,
Alex Loiko64cb83b2018-07-02 13:38:19 +0200182 VolumeCallbacks* volume_callbacks,
183 int startup_min_level,
184 int clipped_level_min,
185 bool use_agc2_level_estimation,
Alex Loiko9489c3a2018-08-09 15:04:24 +0200186 bool disable_digital_adaptive)
Alex Loikoc1676732018-07-02 12:05:28 +0200187 : data_dumper_(new ApmDataDumper(instance_counter_)),
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000188 gctrl_(gctrl),
189 volume_callbacks_(volume_callbacks),
190 frames_since_clipped_(kClippedWaitFrames),
191 level_(0),
192 max_level_(kMaxMicLevel),
193 max_compression_gain_(kMaxCompressionGain),
194 target_compression_(kDefaultCompressionGain),
195 compression_(target_compression_),
196 compression_accumulator_(compression_),
197 capture_muted_(false),
198 check_volume_on_next_process_(true), // Check at startup.
199 startup_(true),
henrikaebf45522019-11-04 13:59:21 +0100200 min_mic_level_(GetMinMicLevel()),
Alex Loiko9489c3a2018-08-09 15:04:24 +0200201 disable_digital_adaptive_(disable_digital_adaptive),
henrikaebf45522019-11-04 13:59:21 +0100202 startup_min_level_(ClampLevel(startup_min_level, min_mic_level_)),
Per Åhgren7c1fb412019-11-07 06:55:35 +0100203 clipped_level_min_(clipped_level_min) {
Alex Loikoc1676732018-07-02 12:05:28 +0200204 instance_counter_++;
Per Åhgrenb8c1be52019-11-07 20:35:50 +0100205 if (use_agc2_level_estimation) {
206 agc_ = std::make_unique<AdaptiveModeLevelEstimatorAgc>(data_dumper_.get());
Alex Loiko2ffafa82018-07-06 15:35:42 +0200207 } else {
Per Åhgrenb8c1be52019-11-07 20:35:50 +0100208 agc_ = std::make_unique<Agc>();
Alex Loiko64cb83b2018-07-02 13:38:19 +0200209 }
Alex Loikoc1676732018-07-02 12:05:28 +0200210}
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000211
212AgcManagerDirect::~AgcManagerDirect() {}
213
214int AgcManagerDirect::Initialize() {
henrikaebf45522019-11-04 13:59:21 +0100215 RTC_DLOG(LS_INFO) << "AgcManagerDirect::Initialize";
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000216 max_level_ = kMaxMicLevel;
217 max_compression_gain_ = kMaxCompressionGain;
Alex Loiko9489c3a2018-08-09 15:04:24 +0200218 target_compression_ = disable_digital_adaptive_ ? 0 : kDefaultCompressionGain;
219 compression_ = disable_digital_adaptive_ ? 0 : target_compression_;
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000220 compression_accumulator_ = compression_;
221 capture_muted_ = false;
222 check_volume_on_next_process_ = true;
223 // TODO(bjornv): Investigate if we need to reset |startup_| as well. For
224 // example, what happens when we change devices.
225
Alex Loikoc1676732018-07-02 12:05:28 +0200226 data_dumper_->InitiateNewSetOfRecordings();
227
Alex Loiko9489c3a2018-08-09 15:04:24 +0200228 return InitializeGainControl(gctrl_, disable_digital_adaptive_);
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000229}
230
Per Åhgren361d1c32019-11-06 22:17:14 +0100231void AgcManagerDirect::AnalyzePreProcess(const float* const* audio,
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000232 int num_channels,
Peter Kastingdce40cf2015-08-24 14:52:23 -0700233 size_t samples_per_channel) {
Per Åhgren361d1c32019-11-06 22:17:14 +0100234 RTC_DCHECK(audio);
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000235 if (capture_muted_) {
236 return;
237 }
238
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000239 if (frames_since_clipped_ < kClippedWaitFrames) {
240 ++frames_since_clipped_;
241 return;
242 }
243
244 // Check for clipped samples, as the AGC has difficulty detecting pitch
245 // under clipping distortion. We do this in the preprocessing phase in order
246 // to catch clipped echo as well.
247 //
248 // If we find a sufficiently clipped frame, drop the current microphone level
249 // and enforce a new maximum level, dropped the same amount from the current
250 // maximum. This harsh treatment is an effort to avoid repeated clipped echo
251 // events. As compensation for this restriction, the maximum compression
252 // gain is increased, through SetMaxLevel().
Per Åhgren361d1c32019-11-06 22:17:14 +0100253 float clipped_ratio =
254 ComputeClippedRatio(audio, num_channels, samples_per_channel);
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000255 if (clipped_ratio > kClippedRatioThreshold) {
Jonas Olsson645b0272018-02-15 15:16:27 +0100256 RTC_DLOG(LS_INFO) << "[agc] Clipping detected. clipped_ratio="
257 << clipped_ratio;
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000258 // Always decrease the maximum level, even if the current level is below
259 // threshold.
henrik.lundinbd681b92016-12-05 09:08:42 -0800260 SetMaxLevel(std::max(clipped_level_min_, max_level_ - kClippedLevelStep));
henrik.lundin30a12fb2016-11-22 07:02:44 -0800261 RTC_HISTOGRAM_BOOLEAN("WebRTC.Audio.AgcClippingAdjustmentAllowed",
henrik.lundinbd681b92016-12-05 09:08:42 -0800262 level_ - kClippedLevelStep >= clipped_level_min_);
263 if (level_ > clipped_level_min_) {
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000264 // Don't try to adjust the level if we're already below the limit. As
265 // a consequence, if the user has brought the level above the limit, we
266 // will still not react until the postproc updates the level.
henrik.lundinbd681b92016-12-05 09:08:42 -0800267 SetLevel(std::max(clipped_level_min_, level_ - kClippedLevelStep));
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000268 // Reset the AGC since the level has changed.
269 agc_->Reset();
270 }
271 frames_since_clipped_ = 0;
272 }
273}
274
Per Åhgren928146f2019-08-20 09:19:21 +0200275void AgcManagerDirect::Process(const float* audio,
Peter Kastingdce40cf2015-08-24 14:52:23 -0700276 size_t length,
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000277 int sample_rate_hz) {
278 if (capture_muted_) {
279 return;
280 }
281
Per Åhgren928146f2019-08-20 09:19:21 +0200282 std::array<int16_t, kMaxNumSamplesPerChannel * kMaxNumChannels> audio_data;
283 const int16_t* audio_fix;
284 size_t safe_length;
285 if (audio) {
286 audio_fix = audio_data.data();
287 safe_length = std::min(audio_data.size(), length);
288 FloatS16ToS16(audio, length, audio_data.data());
289 } else {
290 audio_fix = nullptr;
291 safe_length = length;
292 }
293
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000294 if (check_volume_on_next_process_) {
295 check_volume_on_next_process_ = false;
296 // We have to wait until the first process call to check the volume,
297 // because Chromium doesn't guarantee it to be valid any earlier.
298 CheckVolumeAndReset();
299 }
300
Per Åhgren928146f2019-08-20 09:19:21 +0200301 agc_->Process(audio_fix, safe_length, sample_rate_hz);
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000302
303 UpdateGain();
Alex Loiko9489c3a2018-08-09 15:04:24 +0200304 if (!disable_digital_adaptive_) {
305 UpdateCompressor();
306 }
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000307
Alex Loikoc1676732018-07-02 12:05:28 +0200308 data_dumper_->DumpRaw("experimental_gain_control_compression_gain_db", 1,
309 &compression_);
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000310}
311
312void AgcManagerDirect::SetLevel(int new_level) {
313 int voe_level = volume_callbacks_->GetMicVolume();
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000314 if (voe_level == 0) {
Jonas Olsson645b0272018-02-15 15:16:27 +0100315 RTC_DLOG(LS_INFO)
Mirko Bonadei675513b2017-11-09 11:09:25 +0100316 << "[agc] VolumeCallbacks returned level=0, taking no action.";
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000317 return;
318 }
Jonas Olsson645b0272018-02-15 15:16:27 +0100319 if (voe_level < 0 || voe_level > kMaxMicLevel) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100320 RTC_LOG(LS_ERROR) << "VolumeCallbacks returned an invalid level="
321 << voe_level;
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000322 return;
323 }
324
325 if (voe_level > level_ + kLevelQuantizationSlack ||
326 voe_level < level_ - kLevelQuantizationSlack) {
Jonas Olsson645b0272018-02-15 15:16:27 +0100327 RTC_DLOG(LS_INFO) << "[agc] Mic volume was manually adjusted. Updating "
Yves Gerey665174f2018-06-19 15:03:05 +0200328 "stored level from "
329 << level_ << " to " << voe_level;
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000330 level_ = voe_level;
331 // Always allow the user to increase the volume.
332 if (level_ > max_level_) {
333 SetMaxLevel(level_);
334 }
335 // Take no action in this case, since we can't be sure when the volume
336 // was manually adjusted. The compressor will still provide some of the
337 // desired gain change.
338 agc_->Reset();
339 return;
340 }
341
342 new_level = std::min(new_level, max_level_);
343 if (new_level == level_) {
344 return;
345 }
346
347 volume_callbacks_->SetMicVolume(new_level);
Jonas Olsson645b0272018-02-15 15:16:27 +0100348 RTC_DLOG(LS_INFO) << "[agc] voe_level=" << voe_level << ", "
349 << "level_=" << level_ << ", "
350 << "new_level=" << new_level;
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000351 level_ = new_level;
352}
353
354void AgcManagerDirect::SetMaxLevel(int level) {
henrik.lundinbd681b92016-12-05 09:08:42 -0800355 RTC_DCHECK_GE(level, clipped_level_min_);
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000356 max_level_ = level;
357 // Scale the |kSurplusCompressionGain| linearly across the restricted
358 // level range.
henrik.lundinbd681b92016-12-05 09:08:42 -0800359 max_compression_gain_ =
360 kMaxCompressionGain + std::floor((1.f * kMaxMicLevel - max_level_) /
361 (kMaxMicLevel - clipped_level_min_) *
362 kSurplusCompressionGain +
363 0.5f);
Jonas Olsson645b0272018-02-15 15:16:27 +0100364 RTC_DLOG(LS_INFO) << "[agc] max_level_=" << max_level_
365 << ", max_compression_gain_=" << max_compression_gain_;
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000366}
367
368void AgcManagerDirect::SetCaptureMuted(bool muted) {
369 if (capture_muted_ == muted) {
370 return;
371 }
372 capture_muted_ = muted;
373
374 if (!muted) {
375 // When we unmute, we should reset things to be safe.
376 check_volume_on_next_process_ = true;
377 }
378}
379
380float AgcManagerDirect::voice_probability() {
aluebsecf6b812015-06-25 12:28:48 -0700381 return agc_->voice_probability();
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000382}
383
384int AgcManagerDirect::CheckVolumeAndReset() {
385 int level = volume_callbacks_->GetMicVolume();
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000386 // Reasons for taking action at startup:
387 // 1) A person starting a call is expected to be heard.
388 // 2) Independent of interpretation of |level| == 0 we should raise it so the
389 // AGC can do its job properly.
390 if (level == 0 && !startup_) {
Jonas Olsson645b0272018-02-15 15:16:27 +0100391 RTC_DLOG(LS_INFO)
Mirko Bonadei675513b2017-11-09 11:09:25 +0100392 << "[agc] VolumeCallbacks returned level=0, taking no action.";
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000393 return 0;
394 }
Jonas Olsson645b0272018-02-15 15:16:27 +0100395 if (level < 0 || level > kMaxMicLevel) {
396 RTC_LOG(LS_ERROR) << "[agc] VolumeCallbacks returned an invalid level="
397 << level;
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000398 return -1;
399 }
Jonas Olsson645b0272018-02-15 15:16:27 +0100400 RTC_DLOG(LS_INFO) << "[agc] Initial GetMicVolume()=" << level;
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000401
henrikaebf45522019-11-04 13:59:21 +0100402 int minLevel = startup_ ? startup_min_level_ : min_mic_level_;
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000403 if (level < minLevel) {
404 level = minLevel;
Jonas Olsson645b0272018-02-15 15:16:27 +0100405 RTC_DLOG(LS_INFO) << "[agc] Initial volume too low, raising to " << level;
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000406 volume_callbacks_->SetMicVolume(level);
407 }
408 agc_->Reset();
409 level_ = level;
410 startup_ = false;
411 return 0;
412}
413
414// Requests the RMS error from AGC and distributes the required gain change
415// between the digital compression stage and volume slider. We use the
416// compressor first, providing a slack region around the current slider
417// position to reduce movement.
418//
419// If the slider needs to be moved, we check first if the user has adjusted
420// it, in which case we take no action and cache the updated level.
421void AgcManagerDirect::UpdateGain() {
422 int rms_error = 0;
423 if (!agc_->GetRmsErrorDb(&rms_error)) {
424 // No error update ready.
425 return;
426 }
427 // The compressor will always add at least kMinCompressionGain. In effect,
428 // this adjusts our target gain upward by the same amount and rms_error
429 // needs to reflect that.
430 rms_error += kMinCompressionGain;
431
432 // Handle as much error as possible with the compressor first.
kwiberg07038562017-06-12 11:40:47 -0700433 int raw_compression =
434 rtc::SafeClamp(rms_error, kMinCompressionGain, max_compression_gain_);
435
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000436 // Deemphasize the compression gain error. Move halfway between the current
437 // target and the newly received target. This serves to soften perceptible
438 // intra-talkspurt adjustments, at the cost of some adaptation speed.
439 if ((raw_compression == max_compression_gain_ &&
Yves Gerey665174f2018-06-19 15:03:05 +0200440 target_compression_ == max_compression_gain_ - 1) ||
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000441 (raw_compression == kMinCompressionGain &&
Yves Gerey665174f2018-06-19 15:03:05 +0200442 target_compression_ == kMinCompressionGain + 1)) {
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000443 // Special case to allow the target to reach the endpoints of the
444 // compression range. The deemphasis would otherwise halt it at 1 dB shy.
445 target_compression_ = raw_compression;
446 } else {
Yves Gerey665174f2018-06-19 15:03:05 +0200447 target_compression_ =
448 (raw_compression - target_compression_) / 2 + target_compression_;
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000449 }
450
451 // Residual error will be handled by adjusting the volume slider. Use the
452 // raw rather than deemphasized compression here as we would otherwise
453 // shrink the amount of slack the compressor provides.
kwiberg07038562017-06-12 11:40:47 -0700454 const int residual_gain =
455 rtc::SafeClamp(rms_error - raw_compression, -kMaxResidualGainChange,
456 kMaxResidualGainChange);
Jonas Olsson645b0272018-02-15 15:16:27 +0100457 RTC_DLOG(LS_INFO) << "[agc] rms_error=" << rms_error
458 << ", target_compression=" << target_compression_
459 << ", residual_gain=" << residual_gain;
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000460 if (residual_gain == 0)
461 return;
462
henrik.lundin3edc7f02016-11-24 01:42:46 -0800463 int old_level = level_;
henrikaebf45522019-11-04 13:59:21 +0100464 SetLevel(LevelFromGainError(residual_gain, level_, min_mic_level_));
henrik.lundin3edc7f02016-11-24 01:42:46 -0800465 if (old_level != level_) {
466 // level_ was updated by SetLevel; log the new value.
henrik.lundin45bb5132016-12-06 04:28:04 -0800467 RTC_HISTOGRAM_COUNTS_LINEAR("WebRTC.Audio.AgcSetLevel", level_, 1,
468 kMaxMicLevel, 50);
Alex Loiko99f1e0d2018-07-19 16:39:39 +0200469 // Reset the AGC since the level has changed.
470 agc_->Reset();
henrik.lundin3edc7f02016-11-24 01:42:46 -0800471 }
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000472}
473
474void AgcManagerDirect::UpdateCompressor() {
Alex Loikof3122e02018-08-10 14:43:51 +0200475 calls_since_last_gain_log_++;
476 if (calls_since_last_gain_log_ == 100) {
477 calls_since_last_gain_log_ = 0;
478 RTC_HISTOGRAM_COUNTS_LINEAR("WebRTC.Audio.Agc.DigitalGainApplied",
479 compression_, 0, kMaxCompressionGain,
480 kMaxCompressionGain + 1);
481 }
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000482 if (compression_ == target_compression_) {
483 return;
484 }
485
486 // Adapt the compression gain slowly towards the target, in order to avoid
487 // highly perceptible changes.
488 if (target_compression_ > compression_) {
489 compression_accumulator_ += kCompressionGainStep;
490 } else {
491 compression_accumulator_ -= kCompressionGainStep;
492 }
493
494 // The compressor accepts integer gains in dB. Adjust the gain when
495 // we've come within half a stepsize of the nearest integer. (We don't
496 // check for equality due to potential floating point imprecision).
497 int new_compression = compression_;
498 int nearest_neighbor = std::floor(compression_accumulator_ + 0.5);
499 if (std::fabs(compression_accumulator_ - nearest_neighbor) <
500 kCompressionGainStep / 2) {
501 new_compression = nearest_neighbor;
502 }
503
504 // Set the new compression gain.
505 if (new_compression != compression_) {
Alex Loikof3122e02018-08-10 14:43:51 +0200506 RTC_HISTOGRAM_COUNTS_LINEAR("WebRTC.Audio.Agc.DigitalGainUpdated",
507 new_compression, 0, kMaxCompressionGain,
508 kMaxCompressionGain + 1);
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000509 compression_ = new_compression;
510 compression_accumulator_ = new_compression;
511 if (gctrl_->set_compression_gain_db(compression_) != 0) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100512 RTC_LOG(LS_ERROR) << "set_compression_gain_db(" << compression_
513 << ") failed.";
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000514 }
515 }
516}
517
518} // namespace webrtc