blob: dc6d45121cdd6dbfd0f9fcc018870cafb1efec22 [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
pbos@webrtc.org788acd12014-12-15 09:41:24 +000013#include <cmath>
14
15#ifdef WEBRTC_AGC_DEBUG_DUMP
16#include <cstdio>
17#endif
18
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020019#include "modules/audio_processing/agc/gain_map_internal.h"
Alex Loiko2ffafa82018-07-06 15:35:42 +020020#include "modules/audio_processing/agc2/adaptive_mode_level_estimator_agc.h"
Alex Loikoed8ff642018-07-06 14:54:30 +020021#include "modules/audio_processing/include/gain_control.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020022#include "rtc_base/checks.h"
23#include "rtc_base/logging.h"
Karl Wiberge40468b2017-11-22 10:42:26 +010024#include "rtc_base/numerics/safe_minmax.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
Alex Loikoc1676732018-07-02 12:05:28 +020029int AgcManagerDirect::instance_counter_ = 0;
30
pbos@webrtc.org788acd12014-12-15 09:41:24 +000031namespace {
32
pbos@webrtc.org788acd12014-12-15 09:41:24 +000033// Amount the microphone level is lowered with every clipping event.
34const int kClippedLevelStep = 15;
35// Proportion of clipped samples required to declare a clipping event.
36const float kClippedRatioThreshold = 0.1f;
37// Time in frames to wait after a clipping event before checking again.
38const int kClippedWaitFrames = 300;
39
40// Amount of error we tolerate in the microphone level (presumably due to OS
41// quantization) before we assume the user has manually adjusted the microphone.
42const int kLevelQuantizationSlack = 25;
43
44const int kDefaultCompressionGain = 7;
45const int kMaxCompressionGain = 12;
46const int kMinCompressionGain = 2;
47// Controls the rate of compression changes towards the target.
48const float kCompressionGainStep = 0.05f;
49
50const int kMaxMicLevel = 255;
kwiberg@webrtc.org2ebfac52015-01-14 10:51:54 +000051static_assert(kGainMapSize > kMaxMicLevel, "gain map too small");
pbos@webrtc.org788acd12014-12-15 09:41:24 +000052const int kMinMicLevel = 12;
pbos@webrtc.org788acd12014-12-15 09:41:24 +000053
54// Prevent very large microphone level changes.
55const int kMaxResidualGainChange = 15;
56
57// Maximum additional gain allowed to compensate for microphone level
58// restrictions from clipping events.
59const int kSurplusCompressionGain = 6;
60
Bjorn Volckeradc46c42015-04-15 11:42:40 +020061int ClampLevel(int mic_level) {
kwiberg07038562017-06-12 11:40:47 -070062 return rtc::SafeClamp(mic_level, kMinMicLevel, kMaxMicLevel);
Bjorn Volckeradc46c42015-04-15 11:42:40 +020063}
64
pbos@webrtc.org788acd12014-12-15 09:41:24 +000065int LevelFromGainError(int gain_error, int level) {
kwiberg9e2be5f2016-09-14 05:23:22 -070066 RTC_DCHECK_GE(level, 0);
67 RTC_DCHECK_LE(level, kMaxMicLevel);
pbos@webrtc.org788acd12014-12-15 09:41:24 +000068 if (gain_error == 0) {
69 return level;
70 }
71 // TODO(ajm): Could be made more efficient with a binary search.
72 int new_level = level;
73 if (gain_error > 0) {
74 while (kGainMap[new_level] - kGainMap[level] < gain_error &&
Yves Gerey665174f2018-06-19 15:03:05 +020075 new_level < kMaxMicLevel) {
pbos@webrtc.org788acd12014-12-15 09:41:24 +000076 ++new_level;
77 }
78 } else {
79 while (kGainMap[new_level] - kGainMap[level] > gain_error &&
Yves Gerey665174f2018-06-19 15:03:05 +020080 new_level > kMinMicLevel) {
pbos@webrtc.org788acd12014-12-15 09:41:24 +000081 --new_level;
82 }
83 }
84 return new_level;
85}
86
Alex Loiko9489c3a2018-08-09 15:04:24 +020087int InitializeGainControl(GainControl* gain_control,
88 bool disable_digital_adaptive) {
89 if (gain_control->set_mode(GainControl::kFixedDigital) != 0) {
90 RTC_LOG(LS_ERROR) << "set_mode(GainControl::kFixedDigital) failed.";
91 return -1;
92 }
93 const int target_level_dbfs = disable_digital_adaptive ? 0 : 2;
94 if (gain_control->set_target_level_dbfs(target_level_dbfs) != 0) {
95 RTC_LOG(LS_ERROR) << "set_target_level_dbfs() failed.";
96 return -1;
97 }
98 const int compression_gain_db =
99 disable_digital_adaptive ? 0 : kDefaultCompressionGain;
100 if (gain_control->set_compression_gain_db(compression_gain_db) != 0) {
101 RTC_LOG(LS_ERROR) << "set_compression_gain_db() failed.";
102 return -1;
103 }
104 const bool enable_limiter = !disable_digital_adaptive;
105 if (gain_control->enable_limiter(enable_limiter) != 0) {
106 RTC_LOG(LS_ERROR) << "enable_limiter() failed.";
107 return -1;
108 }
109 return 0;
110}
111
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000112} // namespace
113
114// Facility for dumping debug audio files. All methods are no-ops in the
115// default case where WEBRTC_AGC_DEBUG_DUMP is undefined.
116class DebugFile {
117#ifdef WEBRTC_AGC_DEBUG_DUMP
118 public:
Yves Gerey665174f2018-06-19 15:03:05 +0200119 explicit DebugFile(const char* filename) : file_(fopen(filename, "wb")) {
kwiberg9e2be5f2016-09-14 05:23:22 -0700120 RTC_DCHECK(file_);
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000121 }
Yves Gerey665174f2018-06-19 15:03:05 +0200122 ~DebugFile() { fclose(file_); }
Peter Kastingdce40cf2015-08-24 14:52:23 -0700123 void Write(const int16_t* data, size_t length_samples) {
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000124 fwrite(data, 1, length_samples * sizeof(int16_t), file_);
125 }
Yves Gerey665174f2018-06-19 15:03:05 +0200126
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000127 private:
128 FILE* file_;
129#else
130 public:
Yves Gerey665174f2018-06-19 15:03:05 +0200131 explicit DebugFile(const char* filename) {}
132 ~DebugFile() {}
133 void Write(const int16_t* data, size_t length_samples) {}
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000134#endif // WEBRTC_AGC_DEBUG_DUMP
135};
136
137AgcManagerDirect::AgcManagerDirect(GainControl* gctrl,
Bjorn Volckeradc46c42015-04-15 11:42:40 +0200138 VolumeCallbacks* volume_callbacks,
henrik.lundinbd681b92016-12-05 09:08:42 -0800139 int startup_min_level,
Alex Loiko64cb83b2018-07-02 13:38:19 +0200140 int clipped_level_min,
141 bool use_agc2_level_estimation,
Alex Loiko9489c3a2018-08-09 15:04:24 +0200142 bool disable_digital_adaptive)
Alex Loiko99f1e0d2018-07-19 16:39:39 +0200143 : AgcManagerDirect(use_agc2_level_estimation ? nullptr : new Agc(),
Alex Loiko64cb83b2018-07-02 13:38:19 +0200144 gctrl,
145 volume_callbacks,
146 startup_min_level,
147 clipped_level_min,
148 use_agc2_level_estimation,
Alex Loiko9489c3a2018-08-09 15:04:24 +0200149 disable_digital_adaptive) {
Alex Loiko99f1e0d2018-07-19 16:39:39 +0200150 RTC_DCHECK(agc_);
151}
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000152
153AgcManagerDirect::AgcManagerDirect(Agc* agc,
154 GainControl* gctrl,
Bjorn Volckeradc46c42015-04-15 11:42:40 +0200155 VolumeCallbacks* volume_callbacks,
henrik.lundinbd681b92016-12-05 09:08:42 -0800156 int startup_min_level,
157 int clipped_level_min)
Alex Loiko64cb83b2018-07-02 13:38:19 +0200158 : AgcManagerDirect(agc,
159 gctrl,
160 volume_callbacks,
161 startup_min_level,
162 clipped_level_min,
163 false,
Alex Loiko99f1e0d2018-07-19 16:39:39 +0200164 false) {
165 RTC_DCHECK(agc_);
166}
Alex Loiko64cb83b2018-07-02 13:38:19 +0200167
168AgcManagerDirect::AgcManagerDirect(Agc* agc,
169 GainControl* gctrl,
170 VolumeCallbacks* volume_callbacks,
171 int startup_min_level,
172 int clipped_level_min,
173 bool use_agc2_level_estimation,
Alex Loiko9489c3a2018-08-09 15:04:24 +0200174 bool disable_digital_adaptive)
Alex Loikoc1676732018-07-02 12:05:28 +0200175 : data_dumper_(new ApmDataDumper(instance_counter_)),
176 agc_(agc),
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000177 gctrl_(gctrl),
178 volume_callbacks_(volume_callbacks),
179 frames_since_clipped_(kClippedWaitFrames),
180 level_(0),
181 max_level_(kMaxMicLevel),
182 max_compression_gain_(kMaxCompressionGain),
183 target_compression_(kDefaultCompressionGain),
184 compression_(target_compression_),
185 compression_accumulator_(compression_),
186 capture_muted_(false),
187 check_volume_on_next_process_(true), // Check at startup.
188 startup_(true),
Alex Loiko64cb83b2018-07-02 13:38:19 +0200189 use_agc2_level_estimation_(use_agc2_level_estimation),
Alex Loiko9489c3a2018-08-09 15:04:24 +0200190 disable_digital_adaptive_(disable_digital_adaptive),
Bjorn Volckeradc46c42015-04-15 11:42:40 +0200191 startup_min_level_(ClampLevel(startup_min_level)),
henrik.lundinbd681b92016-12-05 09:08:42 -0800192 clipped_level_min_(clipped_level_min),
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000193 file_preproc_(new DebugFile("agc_preproc.pcm")),
Alex Loikoc1676732018-07-02 12:05:28 +0200194 file_postproc_(new DebugFile("agc_postproc.pcm")) {
195 instance_counter_++;
Alex Loiko64cb83b2018-07-02 13:38:19 +0200196 if (use_agc2_level_estimation_) {
Alex Loiko2ffafa82018-07-06 15:35:42 +0200197 RTC_DCHECK(!agc);
198 agc_.reset(new AdaptiveModeLevelEstimatorAgc(data_dumper_.get()));
199 } else {
200 RTC_DCHECK(agc);
Alex Loiko64cb83b2018-07-02 13:38:19 +0200201 }
Alex Loikoc1676732018-07-02 12:05:28 +0200202}
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000203
204AgcManagerDirect::~AgcManagerDirect() {}
205
206int AgcManagerDirect::Initialize() {
207 max_level_ = kMaxMicLevel;
208 max_compression_gain_ = kMaxCompressionGain;
Alex Loiko9489c3a2018-08-09 15:04:24 +0200209 target_compression_ = disable_digital_adaptive_ ? 0 : kDefaultCompressionGain;
210 compression_ = disable_digital_adaptive_ ? 0 : target_compression_;
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000211 compression_accumulator_ = compression_;
212 capture_muted_ = false;
213 check_volume_on_next_process_ = true;
214 // TODO(bjornv): Investigate if we need to reset |startup_| as well. For
215 // example, what happens when we change devices.
216
Alex Loikoc1676732018-07-02 12:05:28 +0200217 data_dumper_->InitiateNewSetOfRecordings();
218
Alex Loiko9489c3a2018-08-09 15:04:24 +0200219 return InitializeGainControl(gctrl_, disable_digital_adaptive_);
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000220}
221
222void AgcManagerDirect::AnalyzePreProcess(int16_t* audio,
223 int num_channels,
Peter Kastingdce40cf2015-08-24 14:52:23 -0700224 size_t samples_per_channel) {
225 size_t length = num_channels * samples_per_channel;
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000226 if (capture_muted_) {
227 return;
228 }
229
230 file_preproc_->Write(audio, length);
231
232 if (frames_since_clipped_ < kClippedWaitFrames) {
233 ++frames_since_clipped_;
234 return;
235 }
236
237 // Check for clipped samples, as the AGC has difficulty detecting pitch
238 // under clipping distortion. We do this in the preprocessing phase in order
239 // to catch clipped echo as well.
240 //
241 // If we find a sufficiently clipped frame, drop the current microphone level
242 // and enforce a new maximum level, dropped the same amount from the current
243 // maximum. This harsh treatment is an effort to avoid repeated clipped echo
244 // events. As compensation for this restriction, the maximum compression
245 // gain is increased, through SetMaxLevel().
246 float clipped_ratio = agc_->AnalyzePreproc(audio, length);
247 if (clipped_ratio > kClippedRatioThreshold) {
Jonas Olsson645b0272018-02-15 15:16:27 +0100248 RTC_DLOG(LS_INFO) << "[agc] Clipping detected. clipped_ratio="
249 << clipped_ratio;
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000250 // Always decrease the maximum level, even if the current level is below
251 // threshold.
henrik.lundinbd681b92016-12-05 09:08:42 -0800252 SetMaxLevel(std::max(clipped_level_min_, max_level_ - kClippedLevelStep));
henrik.lundin30a12fb2016-11-22 07:02:44 -0800253 RTC_HISTOGRAM_BOOLEAN("WebRTC.Audio.AgcClippingAdjustmentAllowed",
henrik.lundinbd681b92016-12-05 09:08:42 -0800254 level_ - kClippedLevelStep >= clipped_level_min_);
255 if (level_ > clipped_level_min_) {
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000256 // Don't try to adjust the level if we're already below the limit. As
257 // a consequence, if the user has brought the level above the limit, we
258 // will still not react until the postproc updates the level.
henrik.lundinbd681b92016-12-05 09:08:42 -0800259 SetLevel(std::max(clipped_level_min_, level_ - kClippedLevelStep));
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000260 // Reset the AGC since the level has changed.
261 agc_->Reset();
262 }
263 frames_since_clipped_ = 0;
264 }
265}
266
267void AgcManagerDirect::Process(const int16_t* audio,
Peter Kastingdce40cf2015-08-24 14:52:23 -0700268 size_t length,
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000269 int sample_rate_hz) {
270 if (capture_muted_) {
271 return;
272 }
273
274 if (check_volume_on_next_process_) {
275 check_volume_on_next_process_ = false;
276 // We have to wait until the first process call to check the volume,
277 // because Chromium doesn't guarantee it to be valid any earlier.
278 CheckVolumeAndReset();
279 }
280
Jonas Olsson645b0272018-02-15 15:16:27 +0100281 agc_->Process(audio, length, sample_rate_hz);
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000282
283 UpdateGain();
Alex Loiko9489c3a2018-08-09 15:04:24 +0200284 if (!disable_digital_adaptive_) {
285 UpdateCompressor();
286 }
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000287
288 file_postproc_->Write(audio, length);
Alex Loikoc1676732018-07-02 12:05:28 +0200289
290 data_dumper_->DumpRaw("experimental_gain_control_compression_gain_db", 1,
291 &compression_);
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000292}
293
294void AgcManagerDirect::SetLevel(int new_level) {
295 int voe_level = volume_callbacks_->GetMicVolume();
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000296 if (voe_level == 0) {
Jonas Olsson645b0272018-02-15 15:16:27 +0100297 RTC_DLOG(LS_INFO)
Mirko Bonadei675513b2017-11-09 11:09:25 +0100298 << "[agc] VolumeCallbacks returned level=0, taking no action.";
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000299 return;
300 }
Jonas Olsson645b0272018-02-15 15:16:27 +0100301 if (voe_level < 0 || voe_level > kMaxMicLevel) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100302 RTC_LOG(LS_ERROR) << "VolumeCallbacks returned an invalid level="
303 << voe_level;
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000304 return;
305 }
306
307 if (voe_level > level_ + kLevelQuantizationSlack ||
308 voe_level < level_ - kLevelQuantizationSlack) {
Jonas Olsson645b0272018-02-15 15:16:27 +0100309 RTC_DLOG(LS_INFO) << "[agc] Mic volume was manually adjusted. Updating "
Yves Gerey665174f2018-06-19 15:03:05 +0200310 "stored level from "
311 << level_ << " to " << voe_level;
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000312 level_ = voe_level;
313 // Always allow the user to increase the volume.
314 if (level_ > max_level_) {
315 SetMaxLevel(level_);
316 }
317 // Take no action in this case, since we can't be sure when the volume
318 // was manually adjusted. The compressor will still provide some of the
319 // desired gain change.
320 agc_->Reset();
321 return;
322 }
323
324 new_level = std::min(new_level, max_level_);
325 if (new_level == level_) {
326 return;
327 }
328
329 volume_callbacks_->SetMicVolume(new_level);
Jonas Olsson645b0272018-02-15 15:16:27 +0100330 RTC_DLOG(LS_INFO) << "[agc] voe_level=" << voe_level << ", "
331 << "level_=" << level_ << ", "
332 << "new_level=" << new_level;
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000333 level_ = new_level;
334}
335
336void AgcManagerDirect::SetMaxLevel(int level) {
henrik.lundinbd681b92016-12-05 09:08:42 -0800337 RTC_DCHECK_GE(level, clipped_level_min_);
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000338 max_level_ = level;
339 // Scale the |kSurplusCompressionGain| linearly across the restricted
340 // level range.
henrik.lundinbd681b92016-12-05 09:08:42 -0800341 max_compression_gain_ =
342 kMaxCompressionGain + std::floor((1.f * kMaxMicLevel - max_level_) /
343 (kMaxMicLevel - clipped_level_min_) *
344 kSurplusCompressionGain +
345 0.5f);
Jonas Olsson645b0272018-02-15 15:16:27 +0100346 RTC_DLOG(LS_INFO) << "[agc] max_level_=" << max_level_
347 << ", max_compression_gain_=" << max_compression_gain_;
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000348}
349
350void AgcManagerDirect::SetCaptureMuted(bool muted) {
351 if (capture_muted_ == muted) {
352 return;
353 }
354 capture_muted_ = muted;
355
356 if (!muted) {
357 // When we unmute, we should reset things to be safe.
358 check_volume_on_next_process_ = true;
359 }
360}
361
362float AgcManagerDirect::voice_probability() {
aluebsecf6b812015-06-25 12:28:48 -0700363 return agc_->voice_probability();
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000364}
365
366int AgcManagerDirect::CheckVolumeAndReset() {
367 int level = volume_callbacks_->GetMicVolume();
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000368 // Reasons for taking action at startup:
369 // 1) A person starting a call is expected to be heard.
370 // 2) Independent of interpretation of |level| == 0 we should raise it so the
371 // AGC can do its job properly.
372 if (level == 0 && !startup_) {
Jonas Olsson645b0272018-02-15 15:16:27 +0100373 RTC_DLOG(LS_INFO)
Mirko Bonadei675513b2017-11-09 11:09:25 +0100374 << "[agc] VolumeCallbacks returned level=0, taking no action.";
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000375 return 0;
376 }
Jonas Olsson645b0272018-02-15 15:16:27 +0100377 if (level < 0 || level > kMaxMicLevel) {
378 RTC_LOG(LS_ERROR) << "[agc] VolumeCallbacks returned an invalid level="
379 << level;
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000380 return -1;
381 }
Jonas Olsson645b0272018-02-15 15:16:27 +0100382 RTC_DLOG(LS_INFO) << "[agc] Initial GetMicVolume()=" << level;
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000383
Bjorn Volckeradc46c42015-04-15 11:42:40 +0200384 int minLevel = startup_ ? startup_min_level_ : kMinMicLevel;
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000385 if (level < minLevel) {
386 level = minLevel;
Jonas Olsson645b0272018-02-15 15:16:27 +0100387 RTC_DLOG(LS_INFO) << "[agc] Initial volume too low, raising to " << level;
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000388 volume_callbacks_->SetMicVolume(level);
389 }
390 agc_->Reset();
391 level_ = level;
392 startup_ = false;
393 return 0;
394}
395
396// Requests the RMS error from AGC and distributes the required gain change
397// between the digital compression stage and volume slider. We use the
398// compressor first, providing a slack region around the current slider
399// position to reduce movement.
400//
401// If the slider needs to be moved, we check first if the user has adjusted
402// it, in which case we take no action and cache the updated level.
403void AgcManagerDirect::UpdateGain() {
404 int rms_error = 0;
405 if (!agc_->GetRmsErrorDb(&rms_error)) {
406 // No error update ready.
407 return;
408 }
409 // The compressor will always add at least kMinCompressionGain. In effect,
410 // this adjusts our target gain upward by the same amount and rms_error
411 // needs to reflect that.
412 rms_error += kMinCompressionGain;
413
414 // Handle as much error as possible with the compressor first.
kwiberg07038562017-06-12 11:40:47 -0700415 int raw_compression =
416 rtc::SafeClamp(rms_error, kMinCompressionGain, max_compression_gain_);
417
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000418 // Deemphasize the compression gain error. Move halfway between the current
419 // target and the newly received target. This serves to soften perceptible
420 // intra-talkspurt adjustments, at the cost of some adaptation speed.
421 if ((raw_compression == max_compression_gain_ &&
Yves Gerey665174f2018-06-19 15:03:05 +0200422 target_compression_ == max_compression_gain_ - 1) ||
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000423 (raw_compression == kMinCompressionGain &&
Yves Gerey665174f2018-06-19 15:03:05 +0200424 target_compression_ == kMinCompressionGain + 1)) {
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000425 // Special case to allow the target to reach the endpoints of the
426 // compression range. The deemphasis would otherwise halt it at 1 dB shy.
427 target_compression_ = raw_compression;
428 } else {
Yves Gerey665174f2018-06-19 15:03:05 +0200429 target_compression_ =
430 (raw_compression - target_compression_) / 2 + target_compression_;
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000431 }
432
433 // Residual error will be handled by adjusting the volume slider. Use the
434 // raw rather than deemphasized compression here as we would otherwise
435 // shrink the amount of slack the compressor provides.
kwiberg07038562017-06-12 11:40:47 -0700436 const int residual_gain =
437 rtc::SafeClamp(rms_error - raw_compression, -kMaxResidualGainChange,
438 kMaxResidualGainChange);
Jonas Olsson645b0272018-02-15 15:16:27 +0100439 RTC_DLOG(LS_INFO) << "[agc] rms_error=" << rms_error
440 << ", target_compression=" << target_compression_
441 << ", residual_gain=" << residual_gain;
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000442 if (residual_gain == 0)
443 return;
444
henrik.lundin3edc7f02016-11-24 01:42:46 -0800445 int old_level = level_;
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000446 SetLevel(LevelFromGainError(residual_gain, level_));
henrik.lundin3edc7f02016-11-24 01:42:46 -0800447 if (old_level != level_) {
448 // level_ was updated by SetLevel; log the new value.
henrik.lundin45bb5132016-12-06 04:28:04 -0800449 RTC_HISTOGRAM_COUNTS_LINEAR("WebRTC.Audio.AgcSetLevel", level_, 1,
450 kMaxMicLevel, 50);
Alex Loiko99f1e0d2018-07-19 16:39:39 +0200451 // Reset the AGC since the level has changed.
452 agc_->Reset();
henrik.lundin3edc7f02016-11-24 01:42:46 -0800453 }
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000454}
455
456void AgcManagerDirect::UpdateCompressor() {
Alex Loikof3122e02018-08-10 14:43:51 +0200457 calls_since_last_gain_log_++;
458 if (calls_since_last_gain_log_ == 100) {
459 calls_since_last_gain_log_ = 0;
460 RTC_HISTOGRAM_COUNTS_LINEAR("WebRTC.Audio.Agc.DigitalGainApplied",
461 compression_, 0, kMaxCompressionGain,
462 kMaxCompressionGain + 1);
463 }
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000464 if (compression_ == target_compression_) {
465 return;
466 }
467
468 // Adapt the compression gain slowly towards the target, in order to avoid
469 // highly perceptible changes.
470 if (target_compression_ > compression_) {
471 compression_accumulator_ += kCompressionGainStep;
472 } else {
473 compression_accumulator_ -= kCompressionGainStep;
474 }
475
476 // The compressor accepts integer gains in dB. Adjust the gain when
477 // we've come within half a stepsize of the nearest integer. (We don't
478 // check for equality due to potential floating point imprecision).
479 int new_compression = compression_;
480 int nearest_neighbor = std::floor(compression_accumulator_ + 0.5);
481 if (std::fabs(compression_accumulator_ - nearest_neighbor) <
482 kCompressionGainStep / 2) {
483 new_compression = nearest_neighbor;
484 }
485
486 // Set the new compression gain.
487 if (new_compression != compression_) {
Alex Loikof3122e02018-08-10 14:43:51 +0200488 RTC_HISTOGRAM_COUNTS_LINEAR("WebRTC.Audio.Agc.DigitalGainUpdated",
489 new_compression, 0, kMaxCompressionGain,
490 kMaxCompressionGain + 1);
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000491 compression_ = new_compression;
492 compression_accumulator_ = new_compression;
493 if (gctrl_->set_compression_gain_db(compression_) != 0) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100494 RTC_LOG(LS_ERROR) << "set_compression_gain_db(" << compression_
495 << ") failed.";
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000496 }
497 }
498}
499
500} // namespace webrtc