blob: 5c4deeccbf29c5e93ab01745bd643d3b7a5880ae [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
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020020#include "modules/audio_processing/agc/gain_map_internal.h"
Alex Loiko2ffafa82018-07-06 15:35:42 +020021#include "modules/audio_processing/agc2/adaptive_mode_level_estimator_agc.h"
Alex Loikoed8ff642018-07-06 14:54:30 +020022#include "modules/audio_processing/include/gain_control.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020023#include "rtc_base/checks.h"
24#include "rtc_base/logging.h"
Karl Wiberge40468b2017-11-22 10:42:26 +010025#include "rtc_base/numerics/safe_minmax.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020026#include "system_wrappers/include/metrics.h"
pbos@webrtc.org788acd12014-12-15 09:41:24 +000027
28namespace webrtc {
29
Alex Loikoc1676732018-07-02 12:05:28 +020030int AgcManagerDirect::instance_counter_ = 0;
31
pbos@webrtc.org788acd12014-12-15 09:41:24 +000032namespace {
33
pbos@webrtc.org788acd12014-12-15 09:41:24 +000034// Amount the microphone level is lowered with every clipping event.
35const int kClippedLevelStep = 15;
36// Proportion of clipped samples required to declare a clipping event.
37const float kClippedRatioThreshold = 0.1f;
38// Time in frames to wait after a clipping event before checking again.
39const int kClippedWaitFrames = 300;
40
41// Amount of error we tolerate in the microphone level (presumably due to OS
42// quantization) before we assume the user has manually adjusted the microphone.
43const int kLevelQuantizationSlack = 25;
44
45const int kDefaultCompressionGain = 7;
46const int kMaxCompressionGain = 12;
47const int kMinCompressionGain = 2;
48// Controls the rate of compression changes towards the target.
49const float kCompressionGainStep = 0.05f;
50
51const int kMaxMicLevel = 255;
kwiberg@webrtc.org2ebfac52015-01-14 10:51:54 +000052static_assert(kGainMapSize > kMaxMicLevel, "gain map too small");
pbos@webrtc.org788acd12014-12-15 09:41:24 +000053const int kMinMicLevel = 12;
pbos@webrtc.org788acd12014-12-15 09:41:24 +000054
55// Prevent very large microphone level changes.
56const int kMaxResidualGainChange = 15;
57
58// Maximum additional gain allowed to compensate for microphone level
59// restrictions from clipping events.
60const int kSurplusCompressionGain = 6;
61
Bjorn Volckeradc46c42015-04-15 11:42:40 +020062int ClampLevel(int mic_level) {
kwiberg07038562017-06-12 11:40:47 -070063 return rtc::SafeClamp(mic_level, kMinMicLevel, kMaxMicLevel);
Bjorn Volckeradc46c42015-04-15 11:42:40 +020064}
65
pbos@webrtc.org788acd12014-12-15 09:41:24 +000066int LevelFromGainError(int gain_error, int level) {
kwiberg9e2be5f2016-09-14 05:23:22 -070067 RTC_DCHECK_GE(level, 0);
68 RTC_DCHECK_LE(level, kMaxMicLevel);
pbos@webrtc.org788acd12014-12-15 09:41:24 +000069 if (gain_error == 0) {
70 return level;
71 }
72 // TODO(ajm): Could be made more efficient with a binary search.
73 int new_level = level;
74 if (gain_error > 0) {
75 while (kGainMap[new_level] - kGainMap[level] < gain_error &&
Yves Gerey665174f2018-06-19 15:03:05 +020076 new_level < kMaxMicLevel) {
pbos@webrtc.org788acd12014-12-15 09:41:24 +000077 ++new_level;
78 }
79 } else {
80 while (kGainMap[new_level] - kGainMap[level] > gain_error &&
Yves Gerey665174f2018-06-19 15:03:05 +020081 new_level > kMinMicLevel) {
pbos@webrtc.org788acd12014-12-15 09:41:24 +000082 --new_level;
83 }
84 }
85 return new_level;
86}
87
Alex Loiko9489c3a2018-08-09 15:04:24 +020088int InitializeGainControl(GainControl* gain_control,
89 bool disable_digital_adaptive) {
90 if (gain_control->set_mode(GainControl::kFixedDigital) != 0) {
91 RTC_LOG(LS_ERROR) << "set_mode(GainControl::kFixedDigital) failed.";
92 return -1;
93 }
94 const int target_level_dbfs = disable_digital_adaptive ? 0 : 2;
95 if (gain_control->set_target_level_dbfs(target_level_dbfs) != 0) {
96 RTC_LOG(LS_ERROR) << "set_target_level_dbfs() failed.";
97 return -1;
98 }
99 const int compression_gain_db =
100 disable_digital_adaptive ? 0 : kDefaultCompressionGain;
101 if (gain_control->set_compression_gain_db(compression_gain_db) != 0) {
102 RTC_LOG(LS_ERROR) << "set_compression_gain_db() failed.";
103 return -1;
104 }
105 const bool enable_limiter = !disable_digital_adaptive;
106 if (gain_control->enable_limiter(enable_limiter) != 0) {
107 RTC_LOG(LS_ERROR) << "enable_limiter() failed.";
108 return -1;
109 }
110 return 0;
111}
112
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000113} // namespace
114
115// Facility for dumping debug audio files. All methods are no-ops in the
116// default case where WEBRTC_AGC_DEBUG_DUMP is undefined.
117class DebugFile {
118#ifdef WEBRTC_AGC_DEBUG_DUMP
119 public:
Yves Gerey665174f2018-06-19 15:03:05 +0200120 explicit DebugFile(const char* filename) : file_(fopen(filename, "wb")) {
kwiberg9e2be5f2016-09-14 05:23:22 -0700121 RTC_DCHECK(file_);
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000122 }
Yves Gerey665174f2018-06-19 15:03:05 +0200123 ~DebugFile() { fclose(file_); }
Peter Kastingdce40cf2015-08-24 14:52:23 -0700124 void Write(const int16_t* data, size_t length_samples) {
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000125 fwrite(data, 1, length_samples * sizeof(int16_t), file_);
126 }
Yves Gerey665174f2018-06-19 15:03:05 +0200127
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000128 private:
129 FILE* file_;
130#else
131 public:
Yves Gerey665174f2018-06-19 15:03:05 +0200132 explicit DebugFile(const char* filename) {}
133 ~DebugFile() {}
134 void Write(const int16_t* data, size_t length_samples) {}
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000135#endif // WEBRTC_AGC_DEBUG_DUMP
136};
137
138AgcManagerDirect::AgcManagerDirect(GainControl* gctrl,
Bjorn Volckeradc46c42015-04-15 11:42:40 +0200139 VolumeCallbacks* volume_callbacks,
henrik.lundinbd681b92016-12-05 09:08:42 -0800140 int startup_min_level,
Alex Loiko64cb83b2018-07-02 13:38:19 +0200141 int clipped_level_min,
142 bool use_agc2_level_estimation,
Alex Loiko9489c3a2018-08-09 15:04:24 +0200143 bool disable_digital_adaptive)
Alex Loiko99f1e0d2018-07-19 16:39:39 +0200144 : AgcManagerDirect(use_agc2_level_estimation ? nullptr : new Agc(),
Alex Loiko64cb83b2018-07-02 13:38:19 +0200145 gctrl,
146 volume_callbacks,
147 startup_min_level,
148 clipped_level_min,
149 use_agc2_level_estimation,
Alex Loiko9489c3a2018-08-09 15:04:24 +0200150 disable_digital_adaptive) {
Alex Loiko99f1e0d2018-07-19 16:39:39 +0200151 RTC_DCHECK(agc_);
152}
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000153
154AgcManagerDirect::AgcManagerDirect(Agc* agc,
155 GainControl* gctrl,
Bjorn Volckeradc46c42015-04-15 11:42:40 +0200156 VolumeCallbacks* volume_callbacks,
henrik.lundinbd681b92016-12-05 09:08:42 -0800157 int startup_min_level,
158 int clipped_level_min)
Alex Loiko64cb83b2018-07-02 13:38:19 +0200159 : AgcManagerDirect(agc,
160 gctrl,
161 volume_callbacks,
162 startup_min_level,
163 clipped_level_min,
164 false,
Alex Loiko99f1e0d2018-07-19 16:39:39 +0200165 false) {
166 RTC_DCHECK(agc_);
167}
Alex Loiko64cb83b2018-07-02 13:38:19 +0200168
169AgcManagerDirect::AgcManagerDirect(Agc* agc,
170 GainControl* gctrl,
171 VolumeCallbacks* volume_callbacks,
172 int startup_min_level,
173 int clipped_level_min,
174 bool use_agc2_level_estimation,
Alex Loiko9489c3a2018-08-09 15:04:24 +0200175 bool disable_digital_adaptive)
Alex Loikoc1676732018-07-02 12:05:28 +0200176 : data_dumper_(new ApmDataDumper(instance_counter_)),
177 agc_(agc),
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000178 gctrl_(gctrl),
179 volume_callbacks_(volume_callbacks),
180 frames_since_clipped_(kClippedWaitFrames),
181 level_(0),
182 max_level_(kMaxMicLevel),
183 max_compression_gain_(kMaxCompressionGain),
184 target_compression_(kDefaultCompressionGain),
185 compression_(target_compression_),
186 compression_accumulator_(compression_),
187 capture_muted_(false),
188 check_volume_on_next_process_(true), // Check at startup.
189 startup_(true),
Alex Loiko64cb83b2018-07-02 13:38:19 +0200190 use_agc2_level_estimation_(use_agc2_level_estimation),
Alex Loiko9489c3a2018-08-09 15:04:24 +0200191 disable_digital_adaptive_(disable_digital_adaptive),
Bjorn Volckeradc46c42015-04-15 11:42:40 +0200192 startup_min_level_(ClampLevel(startup_min_level)),
henrik.lundinbd681b92016-12-05 09:08:42 -0800193 clipped_level_min_(clipped_level_min),
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000194 file_preproc_(new DebugFile("agc_preproc.pcm")),
Alex Loikoc1676732018-07-02 12:05:28 +0200195 file_postproc_(new DebugFile("agc_postproc.pcm")) {
196 instance_counter_++;
Alex Loiko64cb83b2018-07-02 13:38:19 +0200197 if (use_agc2_level_estimation_) {
Alex Loiko2ffafa82018-07-06 15:35:42 +0200198 RTC_DCHECK(!agc);
199 agc_.reset(new AdaptiveModeLevelEstimatorAgc(data_dumper_.get()));
200 } else {
201 RTC_DCHECK(agc);
Alex Loiko64cb83b2018-07-02 13:38:19 +0200202 }
Alex Loikoc1676732018-07-02 12:05:28 +0200203}
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000204
205AgcManagerDirect::~AgcManagerDirect() {}
206
207int AgcManagerDirect::Initialize() {
208 max_level_ = kMaxMicLevel;
209 max_compression_gain_ = kMaxCompressionGain;
Alex Loiko9489c3a2018-08-09 15:04:24 +0200210 target_compression_ = disable_digital_adaptive_ ? 0 : kDefaultCompressionGain;
211 compression_ = disable_digital_adaptive_ ? 0 : target_compression_;
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000212 compression_accumulator_ = compression_;
213 capture_muted_ = false;
214 check_volume_on_next_process_ = true;
215 // TODO(bjornv): Investigate if we need to reset |startup_| as well. For
216 // example, what happens when we change devices.
217
Alex Loikoc1676732018-07-02 12:05:28 +0200218 data_dumper_->InitiateNewSetOfRecordings();
219
Alex Loiko9489c3a2018-08-09 15:04:24 +0200220 return InitializeGainControl(gctrl_, disable_digital_adaptive_);
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000221}
222
223void AgcManagerDirect::AnalyzePreProcess(int16_t* audio,
224 int num_channels,
Peter Kastingdce40cf2015-08-24 14:52:23 -0700225 size_t samples_per_channel) {
226 size_t length = num_channels * samples_per_channel;
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000227 if (capture_muted_) {
228 return;
229 }
230
231 file_preproc_->Write(audio, length);
232
233 if (frames_since_clipped_ < kClippedWaitFrames) {
234 ++frames_since_clipped_;
235 return;
236 }
237
238 // Check for clipped samples, as the AGC has difficulty detecting pitch
239 // under clipping distortion. We do this in the preprocessing phase in order
240 // to catch clipped echo as well.
241 //
242 // If we find a sufficiently clipped frame, drop the current microphone level
243 // and enforce a new maximum level, dropped the same amount from the current
244 // maximum. This harsh treatment is an effort to avoid repeated clipped echo
245 // events. As compensation for this restriction, the maximum compression
246 // gain is increased, through SetMaxLevel().
247 float clipped_ratio = agc_->AnalyzePreproc(audio, length);
248 if (clipped_ratio > kClippedRatioThreshold) {
Jonas Olsson645b0272018-02-15 15:16:27 +0100249 RTC_DLOG(LS_INFO) << "[agc] Clipping detected. clipped_ratio="
250 << clipped_ratio;
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000251 // Always decrease the maximum level, even if the current level is below
252 // threshold.
henrik.lundinbd681b92016-12-05 09:08:42 -0800253 SetMaxLevel(std::max(clipped_level_min_, max_level_ - kClippedLevelStep));
henrik.lundin30a12fb2016-11-22 07:02:44 -0800254 RTC_HISTOGRAM_BOOLEAN("WebRTC.Audio.AgcClippingAdjustmentAllowed",
henrik.lundinbd681b92016-12-05 09:08:42 -0800255 level_ - kClippedLevelStep >= clipped_level_min_);
256 if (level_ > clipped_level_min_) {
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000257 // Don't try to adjust the level if we're already below the limit. As
258 // a consequence, if the user has brought the level above the limit, we
259 // will still not react until the postproc updates the level.
henrik.lundinbd681b92016-12-05 09:08:42 -0800260 SetLevel(std::max(clipped_level_min_, level_ - kClippedLevelStep));
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000261 // Reset the AGC since the level has changed.
262 agc_->Reset();
263 }
264 frames_since_clipped_ = 0;
265 }
266}
267
268void AgcManagerDirect::Process(const int16_t* audio,
Peter Kastingdce40cf2015-08-24 14:52:23 -0700269 size_t length,
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000270 int sample_rate_hz) {
271 if (capture_muted_) {
272 return;
273 }
274
275 if (check_volume_on_next_process_) {
276 check_volume_on_next_process_ = false;
277 // We have to wait until the first process call to check the volume,
278 // because Chromium doesn't guarantee it to be valid any earlier.
279 CheckVolumeAndReset();
280 }
281
Jonas Olsson645b0272018-02-15 15:16:27 +0100282 agc_->Process(audio, length, sample_rate_hz);
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000283
284 UpdateGain();
Alex Loiko9489c3a2018-08-09 15:04:24 +0200285 if (!disable_digital_adaptive_) {
286 UpdateCompressor();
287 }
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000288
289 file_postproc_->Write(audio, length);
Alex Loikoc1676732018-07-02 12:05:28 +0200290
291 data_dumper_->DumpRaw("experimental_gain_control_compression_gain_db", 1,
292 &compression_);
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000293}
294
295void AgcManagerDirect::SetLevel(int new_level) {
296 int voe_level = volume_callbacks_->GetMicVolume();
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000297 if (voe_level == 0) {
Jonas Olsson645b0272018-02-15 15:16:27 +0100298 RTC_DLOG(LS_INFO)
Mirko Bonadei675513b2017-11-09 11:09:25 +0100299 << "[agc] VolumeCallbacks returned level=0, taking no action.";
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000300 return;
301 }
Jonas Olsson645b0272018-02-15 15:16:27 +0100302 if (voe_level < 0 || voe_level > kMaxMicLevel) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100303 RTC_LOG(LS_ERROR) << "VolumeCallbacks returned an invalid level="
304 << voe_level;
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000305 return;
306 }
307
308 if (voe_level > level_ + kLevelQuantizationSlack ||
309 voe_level < level_ - kLevelQuantizationSlack) {
Jonas Olsson645b0272018-02-15 15:16:27 +0100310 RTC_DLOG(LS_INFO) << "[agc] Mic volume was manually adjusted. Updating "
Yves Gerey665174f2018-06-19 15:03:05 +0200311 "stored level from "
312 << level_ << " to " << voe_level;
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000313 level_ = voe_level;
314 // Always allow the user to increase the volume.
315 if (level_ > max_level_) {
316 SetMaxLevel(level_);
317 }
318 // Take no action in this case, since we can't be sure when the volume
319 // was manually adjusted. The compressor will still provide some of the
320 // desired gain change.
321 agc_->Reset();
322 return;
323 }
324
325 new_level = std::min(new_level, max_level_);
326 if (new_level == level_) {
327 return;
328 }
329
330 volume_callbacks_->SetMicVolume(new_level);
Jonas Olsson645b0272018-02-15 15:16:27 +0100331 RTC_DLOG(LS_INFO) << "[agc] voe_level=" << voe_level << ", "
332 << "level_=" << level_ << ", "
333 << "new_level=" << new_level;
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000334 level_ = new_level;
335}
336
337void AgcManagerDirect::SetMaxLevel(int level) {
henrik.lundinbd681b92016-12-05 09:08:42 -0800338 RTC_DCHECK_GE(level, clipped_level_min_);
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000339 max_level_ = level;
340 // Scale the |kSurplusCompressionGain| linearly across the restricted
341 // level range.
henrik.lundinbd681b92016-12-05 09:08:42 -0800342 max_compression_gain_ =
343 kMaxCompressionGain + std::floor((1.f * kMaxMicLevel - max_level_) /
344 (kMaxMicLevel - clipped_level_min_) *
345 kSurplusCompressionGain +
346 0.5f);
Jonas Olsson645b0272018-02-15 15:16:27 +0100347 RTC_DLOG(LS_INFO) << "[agc] max_level_=" << max_level_
348 << ", max_compression_gain_=" << max_compression_gain_;
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000349}
350
351void AgcManagerDirect::SetCaptureMuted(bool muted) {
352 if (capture_muted_ == muted) {
353 return;
354 }
355 capture_muted_ = muted;
356
357 if (!muted) {
358 // When we unmute, we should reset things to be safe.
359 check_volume_on_next_process_ = true;
360 }
361}
362
363float AgcManagerDirect::voice_probability() {
aluebsecf6b812015-06-25 12:28:48 -0700364 return agc_->voice_probability();
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000365}
366
367int AgcManagerDirect::CheckVolumeAndReset() {
368 int level = volume_callbacks_->GetMicVolume();
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000369 // Reasons for taking action at startup:
370 // 1) A person starting a call is expected to be heard.
371 // 2) Independent of interpretation of |level| == 0 we should raise it so the
372 // AGC can do its job properly.
373 if (level == 0 && !startup_) {
Jonas Olsson645b0272018-02-15 15:16:27 +0100374 RTC_DLOG(LS_INFO)
Mirko Bonadei675513b2017-11-09 11:09:25 +0100375 << "[agc] VolumeCallbacks returned level=0, taking no action.";
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000376 return 0;
377 }
Jonas Olsson645b0272018-02-15 15:16:27 +0100378 if (level < 0 || level > kMaxMicLevel) {
379 RTC_LOG(LS_ERROR) << "[agc] VolumeCallbacks returned an invalid level="
380 << level;
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000381 return -1;
382 }
Jonas Olsson645b0272018-02-15 15:16:27 +0100383 RTC_DLOG(LS_INFO) << "[agc] Initial GetMicVolume()=" << level;
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000384
Bjorn Volckeradc46c42015-04-15 11:42:40 +0200385 int minLevel = startup_ ? startup_min_level_ : kMinMicLevel;
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000386 if (level < minLevel) {
387 level = minLevel;
Jonas Olsson645b0272018-02-15 15:16:27 +0100388 RTC_DLOG(LS_INFO) << "[agc] Initial volume too low, raising to " << level;
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000389 volume_callbacks_->SetMicVolume(level);
390 }
391 agc_->Reset();
392 level_ = level;
393 startup_ = false;
394 return 0;
395}
396
397// Requests the RMS error from AGC and distributes the required gain change
398// between the digital compression stage and volume slider. We use the
399// compressor first, providing a slack region around the current slider
400// position to reduce movement.
401//
402// If the slider needs to be moved, we check first if the user has adjusted
403// it, in which case we take no action and cache the updated level.
404void AgcManagerDirect::UpdateGain() {
405 int rms_error = 0;
406 if (!agc_->GetRmsErrorDb(&rms_error)) {
407 // No error update ready.
408 return;
409 }
410 // The compressor will always add at least kMinCompressionGain. In effect,
411 // this adjusts our target gain upward by the same amount and rms_error
412 // needs to reflect that.
413 rms_error += kMinCompressionGain;
414
415 // Handle as much error as possible with the compressor first.
kwiberg07038562017-06-12 11:40:47 -0700416 int raw_compression =
417 rtc::SafeClamp(rms_error, kMinCompressionGain, max_compression_gain_);
418
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000419 // Deemphasize the compression gain error. Move halfway between the current
420 // target and the newly received target. This serves to soften perceptible
421 // intra-talkspurt adjustments, at the cost of some adaptation speed.
422 if ((raw_compression == max_compression_gain_ &&
Yves Gerey665174f2018-06-19 15:03:05 +0200423 target_compression_ == max_compression_gain_ - 1) ||
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000424 (raw_compression == kMinCompressionGain &&
Yves Gerey665174f2018-06-19 15:03:05 +0200425 target_compression_ == kMinCompressionGain + 1)) {
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000426 // Special case to allow the target to reach the endpoints of the
427 // compression range. The deemphasis would otherwise halt it at 1 dB shy.
428 target_compression_ = raw_compression;
429 } else {
Yves Gerey665174f2018-06-19 15:03:05 +0200430 target_compression_ =
431 (raw_compression - target_compression_) / 2 + target_compression_;
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000432 }
433
434 // Residual error will be handled by adjusting the volume slider. Use the
435 // raw rather than deemphasized compression here as we would otherwise
436 // shrink the amount of slack the compressor provides.
kwiberg07038562017-06-12 11:40:47 -0700437 const int residual_gain =
438 rtc::SafeClamp(rms_error - raw_compression, -kMaxResidualGainChange,
439 kMaxResidualGainChange);
Jonas Olsson645b0272018-02-15 15:16:27 +0100440 RTC_DLOG(LS_INFO) << "[agc] rms_error=" << rms_error
441 << ", target_compression=" << target_compression_
442 << ", residual_gain=" << residual_gain;
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000443 if (residual_gain == 0)
444 return;
445
henrik.lundin3edc7f02016-11-24 01:42:46 -0800446 int old_level = level_;
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000447 SetLevel(LevelFromGainError(residual_gain, level_));
henrik.lundin3edc7f02016-11-24 01:42:46 -0800448 if (old_level != level_) {
449 // level_ was updated by SetLevel; log the new value.
henrik.lundin45bb5132016-12-06 04:28:04 -0800450 RTC_HISTOGRAM_COUNTS_LINEAR("WebRTC.Audio.AgcSetLevel", level_, 1,
451 kMaxMicLevel, 50);
Alex Loiko99f1e0d2018-07-19 16:39:39 +0200452 // Reset the AGC since the level has changed.
453 agc_->Reset();
henrik.lundin3edc7f02016-11-24 01:42:46 -0800454 }
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000455}
456
457void AgcManagerDirect::UpdateCompressor() {
Alex Loikof3122e02018-08-10 14:43:51 +0200458 calls_since_last_gain_log_++;
459 if (calls_since_last_gain_log_ == 100) {
460 calls_since_last_gain_log_ = 0;
461 RTC_HISTOGRAM_COUNTS_LINEAR("WebRTC.Audio.Agc.DigitalGainApplied",
462 compression_, 0, kMaxCompressionGain,
463 kMaxCompressionGain + 1);
464 }
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000465 if (compression_ == target_compression_) {
466 return;
467 }
468
469 // Adapt the compression gain slowly towards the target, in order to avoid
470 // highly perceptible changes.
471 if (target_compression_ > compression_) {
472 compression_accumulator_ += kCompressionGainStep;
473 } else {
474 compression_accumulator_ -= kCompressionGainStep;
475 }
476
477 // The compressor accepts integer gains in dB. Adjust the gain when
478 // we've come within half a stepsize of the nearest integer. (We don't
479 // check for equality due to potential floating point imprecision).
480 int new_compression = compression_;
481 int nearest_neighbor = std::floor(compression_accumulator_ + 0.5);
482 if (std::fabs(compression_accumulator_ - nearest_neighbor) <
483 kCompressionGainStep / 2) {
484 new_compression = nearest_neighbor;
485 }
486
487 // Set the new compression gain.
488 if (new_compression != compression_) {
Alex Loikof3122e02018-08-10 14:43:51 +0200489 RTC_HISTOGRAM_COUNTS_LINEAR("WebRTC.Audio.Agc.DigitalGainUpdated",
490 new_compression, 0, kMaxCompressionGain,
491 kMaxCompressionGain + 1);
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000492 compression_ = new_compression;
493 compression_accumulator_ = new_compression;
494 if (gctrl_->set_compression_gain_db(compression_) != 0) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100495 RTC_LOG(LS_ERROR) << "set_compression_gain_db(" << compression_
496 << ") failed.";
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000497 }
498 }
499}
500
501} // namespace webrtc