blob: f91356017bd423b61014e813f5c1da68fb5be141 [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"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020027#include "system_wrappers/include/metrics.h"
pbos@webrtc.org788acd12014-12-15 09:41:24 +000028
29namespace webrtc {
30
Alex Loikoc1676732018-07-02 12:05:28 +020031int AgcManagerDirect::instance_counter_ = 0;
32
pbos@webrtc.org788acd12014-12-15 09:41:24 +000033namespace {
34
pbos@webrtc.org788acd12014-12-15 09:41:24 +000035// Amount the microphone level is lowered with every clipping event.
36const int kClippedLevelStep = 15;
37// Proportion of clipped samples required to declare a clipping event.
38const float kClippedRatioThreshold = 0.1f;
39// Time in frames to wait after a clipping event before checking again.
40const int kClippedWaitFrames = 300;
41
42// Amount of error we tolerate in the microphone level (presumably due to OS
43// quantization) before we assume the user has manually adjusted the microphone.
44const int kLevelQuantizationSlack = 25;
45
46const int kDefaultCompressionGain = 7;
47const int kMaxCompressionGain = 12;
48const int kMinCompressionGain = 2;
49// Controls the rate of compression changes towards the target.
50const float kCompressionGainStep = 0.05f;
51
52const int kMaxMicLevel = 255;
kwiberg@webrtc.org2ebfac52015-01-14 10:51:54 +000053static_assert(kGainMapSize > kMaxMicLevel, "gain map too small");
pbos@webrtc.org788acd12014-12-15 09:41:24 +000054const int kMinMicLevel = 12;
pbos@webrtc.org788acd12014-12-15 09:41:24 +000055
56// Prevent very large microphone level changes.
57const int kMaxResidualGainChange = 15;
58
59// Maximum additional gain allowed to compensate for microphone level
60// restrictions from clipping events.
61const int kSurplusCompressionGain = 6;
62
Per Åhgren928146f2019-08-20 09:19:21 +020063// Maximum number of channels and number of samples per channel supported.
64constexpr size_t kMaxNumSamplesPerChannel = 1920;
65constexpr size_t kMaxNumChannels = 4;
66
Bjorn Volckeradc46c42015-04-15 11:42:40 +020067int ClampLevel(int mic_level) {
kwiberg07038562017-06-12 11:40:47 -070068 return rtc::SafeClamp(mic_level, kMinMicLevel, kMaxMicLevel);
Bjorn Volckeradc46c42015-04-15 11:42:40 +020069}
70
pbos@webrtc.org788acd12014-12-15 09:41:24 +000071int LevelFromGainError(int gain_error, int level) {
kwiberg9e2be5f2016-09-14 05:23:22 -070072 RTC_DCHECK_GE(level, 0);
73 RTC_DCHECK_LE(level, kMaxMicLevel);
pbos@webrtc.org788acd12014-12-15 09:41:24 +000074 if (gain_error == 0) {
75 return level;
76 }
77 // TODO(ajm): Could be made more efficient with a binary search.
78 int new_level = level;
79 if (gain_error > 0) {
80 while (kGainMap[new_level] - kGainMap[level] < gain_error &&
Yves Gerey665174f2018-06-19 15:03:05 +020081 new_level < kMaxMicLevel) {
pbos@webrtc.org788acd12014-12-15 09:41:24 +000082 ++new_level;
83 }
84 } else {
85 while (kGainMap[new_level] - kGainMap[level] > gain_error &&
Yves Gerey665174f2018-06-19 15:03:05 +020086 new_level > kMinMicLevel) {
pbos@webrtc.org788acd12014-12-15 09:41:24 +000087 --new_level;
88 }
89 }
90 return new_level;
91}
92
Alex Loiko9489c3a2018-08-09 15:04:24 +020093int InitializeGainControl(GainControl* gain_control,
94 bool disable_digital_adaptive) {
95 if (gain_control->set_mode(GainControl::kFixedDigital) != 0) {
96 RTC_LOG(LS_ERROR) << "set_mode(GainControl::kFixedDigital) failed.";
97 return -1;
98 }
99 const int target_level_dbfs = disable_digital_adaptive ? 0 : 2;
100 if (gain_control->set_target_level_dbfs(target_level_dbfs) != 0) {
101 RTC_LOG(LS_ERROR) << "set_target_level_dbfs() failed.";
102 return -1;
103 }
104 const int compression_gain_db =
105 disable_digital_adaptive ? 0 : kDefaultCompressionGain;
106 if (gain_control->set_compression_gain_db(compression_gain_db) != 0) {
107 RTC_LOG(LS_ERROR) << "set_compression_gain_db() failed.";
108 return -1;
109 }
110 const bool enable_limiter = !disable_digital_adaptive;
111 if (gain_control->enable_limiter(enable_limiter) != 0) {
112 RTC_LOG(LS_ERROR) << "enable_limiter() failed.";
113 return -1;
114 }
115 return 0;
116}
117
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000118} // namespace
119
120// Facility for dumping debug audio files. All methods are no-ops in the
121// default case where WEBRTC_AGC_DEBUG_DUMP is undefined.
122class DebugFile {
123#ifdef WEBRTC_AGC_DEBUG_DUMP
124 public:
Yves Gerey665174f2018-06-19 15:03:05 +0200125 explicit DebugFile(const char* filename) : file_(fopen(filename, "wb")) {
kwiberg9e2be5f2016-09-14 05:23:22 -0700126 RTC_DCHECK(file_);
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000127 }
Yves Gerey665174f2018-06-19 15:03:05 +0200128 ~DebugFile() { fclose(file_); }
Peter Kastingdce40cf2015-08-24 14:52:23 -0700129 void Write(const int16_t* data, size_t length_samples) {
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000130 fwrite(data, 1, length_samples * sizeof(int16_t), file_);
131 }
Yves Gerey665174f2018-06-19 15:03:05 +0200132
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000133 private:
134 FILE* file_;
135#else
136 public:
Yves Gerey665174f2018-06-19 15:03:05 +0200137 explicit DebugFile(const char* filename) {}
138 ~DebugFile() {}
139 void Write(const int16_t* data, size_t length_samples) {}
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000140#endif // WEBRTC_AGC_DEBUG_DUMP
141};
142
143AgcManagerDirect::AgcManagerDirect(GainControl* gctrl,
Bjorn Volckeradc46c42015-04-15 11:42:40 +0200144 VolumeCallbacks* volume_callbacks,
henrik.lundinbd681b92016-12-05 09:08:42 -0800145 int startup_min_level,
Alex Loiko64cb83b2018-07-02 13:38:19 +0200146 int clipped_level_min,
147 bool use_agc2_level_estimation,
Alex Loiko9489c3a2018-08-09 15:04:24 +0200148 bool disable_digital_adaptive)
Alex Loiko99f1e0d2018-07-19 16:39:39 +0200149 : AgcManagerDirect(use_agc2_level_estimation ? nullptr : new Agc(),
Alex Loiko64cb83b2018-07-02 13:38:19 +0200150 gctrl,
151 volume_callbacks,
152 startup_min_level,
153 clipped_level_min,
154 use_agc2_level_estimation,
Alex Loiko9489c3a2018-08-09 15:04:24 +0200155 disable_digital_adaptive) {
Alex Loiko99f1e0d2018-07-19 16:39:39 +0200156 RTC_DCHECK(agc_);
157}
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000158
159AgcManagerDirect::AgcManagerDirect(Agc* agc,
160 GainControl* gctrl,
Bjorn Volckeradc46c42015-04-15 11:42:40 +0200161 VolumeCallbacks* volume_callbacks,
henrik.lundinbd681b92016-12-05 09:08:42 -0800162 int startup_min_level,
163 int clipped_level_min)
Alex Loiko64cb83b2018-07-02 13:38:19 +0200164 : AgcManagerDirect(agc,
165 gctrl,
166 volume_callbacks,
167 startup_min_level,
168 clipped_level_min,
169 false,
Alex Loiko99f1e0d2018-07-19 16:39:39 +0200170 false) {
171 RTC_DCHECK(agc_);
172}
Alex Loiko64cb83b2018-07-02 13:38:19 +0200173
174AgcManagerDirect::AgcManagerDirect(Agc* agc,
175 GainControl* gctrl,
176 VolumeCallbacks* volume_callbacks,
177 int startup_min_level,
178 int clipped_level_min,
179 bool use_agc2_level_estimation,
Alex Loiko9489c3a2018-08-09 15:04:24 +0200180 bool disable_digital_adaptive)
Alex Loikoc1676732018-07-02 12:05:28 +0200181 : data_dumper_(new ApmDataDumper(instance_counter_)),
182 agc_(agc),
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000183 gctrl_(gctrl),
184 volume_callbacks_(volume_callbacks),
185 frames_since_clipped_(kClippedWaitFrames),
186 level_(0),
187 max_level_(kMaxMicLevel),
188 max_compression_gain_(kMaxCompressionGain),
189 target_compression_(kDefaultCompressionGain),
190 compression_(target_compression_),
191 compression_accumulator_(compression_),
192 capture_muted_(false),
193 check_volume_on_next_process_(true), // Check at startup.
194 startup_(true),
Alex Loiko64cb83b2018-07-02 13:38:19 +0200195 use_agc2_level_estimation_(use_agc2_level_estimation),
Alex Loiko9489c3a2018-08-09 15:04:24 +0200196 disable_digital_adaptive_(disable_digital_adaptive),
Bjorn Volckeradc46c42015-04-15 11:42:40 +0200197 startup_min_level_(ClampLevel(startup_min_level)),
henrik.lundinbd681b92016-12-05 09:08:42 -0800198 clipped_level_min_(clipped_level_min),
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000199 file_preproc_(new DebugFile("agc_preproc.pcm")),
Alex Loikoc1676732018-07-02 12:05:28 +0200200 file_postproc_(new DebugFile("agc_postproc.pcm")) {
201 instance_counter_++;
Alex Loiko64cb83b2018-07-02 13:38:19 +0200202 if (use_agc2_level_estimation_) {
Alex Loiko2ffafa82018-07-06 15:35:42 +0200203 RTC_DCHECK(!agc);
204 agc_.reset(new AdaptiveModeLevelEstimatorAgc(data_dumper_.get()));
205 } else {
206 RTC_DCHECK(agc);
Alex Loiko64cb83b2018-07-02 13:38:19 +0200207 }
Alex Loikoc1676732018-07-02 12:05:28 +0200208}
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000209
210AgcManagerDirect::~AgcManagerDirect() {}
211
212int AgcManagerDirect::Initialize() {
213 max_level_ = kMaxMicLevel;
214 max_compression_gain_ = kMaxCompressionGain;
Alex Loiko9489c3a2018-08-09 15:04:24 +0200215 target_compression_ = disable_digital_adaptive_ ? 0 : kDefaultCompressionGain;
216 compression_ = disable_digital_adaptive_ ? 0 : target_compression_;
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000217 compression_accumulator_ = compression_;
218 capture_muted_ = false;
219 check_volume_on_next_process_ = true;
220 // TODO(bjornv): Investigate if we need to reset |startup_| as well. For
221 // example, what happens when we change devices.
222
Alex Loikoc1676732018-07-02 12:05:28 +0200223 data_dumper_->InitiateNewSetOfRecordings();
224
Alex Loiko9489c3a2018-08-09 15:04:24 +0200225 return InitializeGainControl(gctrl_, disable_digital_adaptive_);
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000226}
227
Per Åhgren928146f2019-08-20 09:19:21 +0200228void AgcManagerDirect::AnalyzePreProcess(float* audio,
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000229 int num_channels,
Peter Kastingdce40cf2015-08-24 14:52:23 -0700230 size_t samples_per_channel) {
231 size_t length = num_channels * samples_per_channel;
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000232 if (capture_muted_) {
233 return;
234 }
235
Per Åhgren928146f2019-08-20 09:19:21 +0200236 std::array<int16_t, kMaxNumSamplesPerChannel * kMaxNumChannels> audio_data;
237 int16_t* audio_fix;
238 size_t safe_length;
239 if (audio) {
240 audio_fix = audio_data.data();
241 safe_length = std::min(audio_data.size(), length);
242 FloatS16ToS16(audio, length, audio_fix);
243 } else {
244 audio_fix = nullptr;
245 safe_length = length;
246 }
247
248 file_preproc_->Write(audio_fix, safe_length);
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000249
250 if (frames_since_clipped_ < kClippedWaitFrames) {
251 ++frames_since_clipped_;
252 return;
253 }
254
255 // Check for clipped samples, as the AGC has difficulty detecting pitch
256 // under clipping distortion. We do this in the preprocessing phase in order
257 // to catch clipped echo as well.
258 //
259 // If we find a sufficiently clipped frame, drop the current microphone level
260 // and enforce a new maximum level, dropped the same amount from the current
261 // maximum. This harsh treatment is an effort to avoid repeated clipped echo
262 // events. As compensation for this restriction, the maximum compression
263 // gain is increased, through SetMaxLevel().
Per Åhgren928146f2019-08-20 09:19:21 +0200264 float clipped_ratio = agc_->AnalyzePreproc(audio_fix, safe_length);
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000265 if (clipped_ratio > kClippedRatioThreshold) {
Jonas Olsson645b0272018-02-15 15:16:27 +0100266 RTC_DLOG(LS_INFO) << "[agc] Clipping detected. clipped_ratio="
267 << clipped_ratio;
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000268 // Always decrease the maximum level, even if the current level is below
269 // threshold.
henrik.lundinbd681b92016-12-05 09:08:42 -0800270 SetMaxLevel(std::max(clipped_level_min_, max_level_ - kClippedLevelStep));
henrik.lundin30a12fb2016-11-22 07:02:44 -0800271 RTC_HISTOGRAM_BOOLEAN("WebRTC.Audio.AgcClippingAdjustmentAllowed",
henrik.lundinbd681b92016-12-05 09:08:42 -0800272 level_ - kClippedLevelStep >= clipped_level_min_);
273 if (level_ > clipped_level_min_) {
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000274 // Don't try to adjust the level if we're already below the limit. As
275 // a consequence, if the user has brought the level above the limit, we
276 // will still not react until the postproc updates the level.
henrik.lundinbd681b92016-12-05 09:08:42 -0800277 SetLevel(std::max(clipped_level_min_, level_ - kClippedLevelStep));
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000278 // Reset the AGC since the level has changed.
279 agc_->Reset();
280 }
281 frames_since_clipped_ = 0;
282 }
Per Åhgren928146f2019-08-20 09:19:21 +0200283
284 if (audio) {
285 S16ToFloatS16(audio_fix, safe_length, audio);
286 }
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000287}
288
Per Åhgren928146f2019-08-20 09:19:21 +0200289void AgcManagerDirect::Process(const float* audio,
Peter Kastingdce40cf2015-08-24 14:52:23 -0700290 size_t length,
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000291 int sample_rate_hz) {
292 if (capture_muted_) {
293 return;
294 }
295
Per Åhgren928146f2019-08-20 09:19:21 +0200296 std::array<int16_t, kMaxNumSamplesPerChannel * kMaxNumChannels> audio_data;
297 const int16_t* audio_fix;
298 size_t safe_length;
299 if (audio) {
300 audio_fix = audio_data.data();
301 safe_length = std::min(audio_data.size(), length);
302 FloatS16ToS16(audio, length, audio_data.data());
303 } else {
304 audio_fix = nullptr;
305 safe_length = length;
306 }
307
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000308 if (check_volume_on_next_process_) {
309 check_volume_on_next_process_ = false;
310 // We have to wait until the first process call to check the volume,
311 // because Chromium doesn't guarantee it to be valid any earlier.
312 CheckVolumeAndReset();
313 }
314
Per Åhgren928146f2019-08-20 09:19:21 +0200315 agc_->Process(audio_fix, safe_length, sample_rate_hz);
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000316
317 UpdateGain();
Alex Loiko9489c3a2018-08-09 15:04:24 +0200318 if (!disable_digital_adaptive_) {
319 UpdateCompressor();
320 }
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000321
Per Åhgren928146f2019-08-20 09:19:21 +0200322 file_postproc_->Write(audio_fix, safe_length);
Alex Loikoc1676732018-07-02 12:05:28 +0200323
324 data_dumper_->DumpRaw("experimental_gain_control_compression_gain_db", 1,
325 &compression_);
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000326}
327
328void AgcManagerDirect::SetLevel(int new_level) {
329 int voe_level = volume_callbacks_->GetMicVolume();
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000330 if (voe_level == 0) {
Jonas Olsson645b0272018-02-15 15:16:27 +0100331 RTC_DLOG(LS_INFO)
Mirko Bonadei675513b2017-11-09 11:09:25 +0100332 << "[agc] VolumeCallbacks returned level=0, taking no action.";
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000333 return;
334 }
Jonas Olsson645b0272018-02-15 15:16:27 +0100335 if (voe_level < 0 || voe_level > kMaxMicLevel) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100336 RTC_LOG(LS_ERROR) << "VolumeCallbacks returned an invalid level="
337 << voe_level;
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000338 return;
339 }
340
341 if (voe_level > level_ + kLevelQuantizationSlack ||
342 voe_level < level_ - kLevelQuantizationSlack) {
Jonas Olsson645b0272018-02-15 15:16:27 +0100343 RTC_DLOG(LS_INFO) << "[agc] Mic volume was manually adjusted. Updating "
Yves Gerey665174f2018-06-19 15:03:05 +0200344 "stored level from "
345 << level_ << " to " << voe_level;
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000346 level_ = voe_level;
347 // Always allow the user to increase the volume.
348 if (level_ > max_level_) {
349 SetMaxLevel(level_);
350 }
351 // Take no action in this case, since we can't be sure when the volume
352 // was manually adjusted. The compressor will still provide some of the
353 // desired gain change.
354 agc_->Reset();
355 return;
356 }
357
358 new_level = std::min(new_level, max_level_);
359 if (new_level == level_) {
360 return;
361 }
362
363 volume_callbacks_->SetMicVolume(new_level);
Jonas Olsson645b0272018-02-15 15:16:27 +0100364 RTC_DLOG(LS_INFO) << "[agc] voe_level=" << voe_level << ", "
365 << "level_=" << level_ << ", "
366 << "new_level=" << new_level;
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000367 level_ = new_level;
368}
369
370void AgcManagerDirect::SetMaxLevel(int level) {
henrik.lundinbd681b92016-12-05 09:08:42 -0800371 RTC_DCHECK_GE(level, clipped_level_min_);
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000372 max_level_ = level;
373 // Scale the |kSurplusCompressionGain| linearly across the restricted
374 // level range.
henrik.lundinbd681b92016-12-05 09:08:42 -0800375 max_compression_gain_ =
376 kMaxCompressionGain + std::floor((1.f * kMaxMicLevel - max_level_) /
377 (kMaxMicLevel - clipped_level_min_) *
378 kSurplusCompressionGain +
379 0.5f);
Jonas Olsson645b0272018-02-15 15:16:27 +0100380 RTC_DLOG(LS_INFO) << "[agc] max_level_=" << max_level_
381 << ", max_compression_gain_=" << max_compression_gain_;
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000382}
383
384void AgcManagerDirect::SetCaptureMuted(bool muted) {
385 if (capture_muted_ == muted) {
386 return;
387 }
388 capture_muted_ = muted;
389
390 if (!muted) {
391 // When we unmute, we should reset things to be safe.
392 check_volume_on_next_process_ = true;
393 }
394}
395
396float AgcManagerDirect::voice_probability() {
aluebsecf6b812015-06-25 12:28:48 -0700397 return agc_->voice_probability();
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000398}
399
400int AgcManagerDirect::CheckVolumeAndReset() {
401 int level = volume_callbacks_->GetMicVolume();
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000402 // Reasons for taking action at startup:
403 // 1) A person starting a call is expected to be heard.
404 // 2) Independent of interpretation of |level| == 0 we should raise it so the
405 // AGC can do its job properly.
406 if (level == 0 && !startup_) {
Jonas Olsson645b0272018-02-15 15:16:27 +0100407 RTC_DLOG(LS_INFO)
Mirko Bonadei675513b2017-11-09 11:09:25 +0100408 << "[agc] VolumeCallbacks returned level=0, taking no action.";
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000409 return 0;
410 }
Jonas Olsson645b0272018-02-15 15:16:27 +0100411 if (level < 0 || level > kMaxMicLevel) {
412 RTC_LOG(LS_ERROR) << "[agc] VolumeCallbacks returned an invalid level="
413 << level;
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000414 return -1;
415 }
Jonas Olsson645b0272018-02-15 15:16:27 +0100416 RTC_DLOG(LS_INFO) << "[agc] Initial GetMicVolume()=" << level;
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000417
Bjorn Volckeradc46c42015-04-15 11:42:40 +0200418 int minLevel = startup_ ? startup_min_level_ : kMinMicLevel;
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000419 if (level < minLevel) {
420 level = minLevel;
Jonas Olsson645b0272018-02-15 15:16:27 +0100421 RTC_DLOG(LS_INFO) << "[agc] Initial volume too low, raising to " << level;
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000422 volume_callbacks_->SetMicVolume(level);
423 }
424 agc_->Reset();
425 level_ = level;
426 startup_ = false;
427 return 0;
428}
429
430// Requests the RMS error from AGC and distributes the required gain change
431// between the digital compression stage and volume slider. We use the
432// compressor first, providing a slack region around the current slider
433// position to reduce movement.
434//
435// If the slider needs to be moved, we check first if the user has adjusted
436// it, in which case we take no action and cache the updated level.
437void AgcManagerDirect::UpdateGain() {
438 int rms_error = 0;
439 if (!agc_->GetRmsErrorDb(&rms_error)) {
440 // No error update ready.
441 return;
442 }
443 // The compressor will always add at least kMinCompressionGain. In effect,
444 // this adjusts our target gain upward by the same amount and rms_error
445 // needs to reflect that.
446 rms_error += kMinCompressionGain;
447
448 // Handle as much error as possible with the compressor first.
kwiberg07038562017-06-12 11:40:47 -0700449 int raw_compression =
450 rtc::SafeClamp(rms_error, kMinCompressionGain, max_compression_gain_);
451
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000452 // Deemphasize the compression gain error. Move halfway between the current
453 // target and the newly received target. This serves to soften perceptible
454 // intra-talkspurt adjustments, at the cost of some adaptation speed.
455 if ((raw_compression == max_compression_gain_ &&
Yves Gerey665174f2018-06-19 15:03:05 +0200456 target_compression_ == max_compression_gain_ - 1) ||
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000457 (raw_compression == kMinCompressionGain &&
Yves Gerey665174f2018-06-19 15:03:05 +0200458 target_compression_ == kMinCompressionGain + 1)) {
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000459 // Special case to allow the target to reach the endpoints of the
460 // compression range. The deemphasis would otherwise halt it at 1 dB shy.
461 target_compression_ = raw_compression;
462 } else {
Yves Gerey665174f2018-06-19 15:03:05 +0200463 target_compression_ =
464 (raw_compression - target_compression_) / 2 + target_compression_;
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000465 }
466
467 // Residual error will be handled by adjusting the volume slider. Use the
468 // raw rather than deemphasized compression here as we would otherwise
469 // shrink the amount of slack the compressor provides.
kwiberg07038562017-06-12 11:40:47 -0700470 const int residual_gain =
471 rtc::SafeClamp(rms_error - raw_compression, -kMaxResidualGainChange,
472 kMaxResidualGainChange);
Jonas Olsson645b0272018-02-15 15:16:27 +0100473 RTC_DLOG(LS_INFO) << "[agc] rms_error=" << rms_error
474 << ", target_compression=" << target_compression_
475 << ", residual_gain=" << residual_gain;
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000476 if (residual_gain == 0)
477 return;
478
henrik.lundin3edc7f02016-11-24 01:42:46 -0800479 int old_level = level_;
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000480 SetLevel(LevelFromGainError(residual_gain, level_));
henrik.lundin3edc7f02016-11-24 01:42:46 -0800481 if (old_level != level_) {
482 // level_ was updated by SetLevel; log the new value.
henrik.lundin45bb5132016-12-06 04:28:04 -0800483 RTC_HISTOGRAM_COUNTS_LINEAR("WebRTC.Audio.AgcSetLevel", level_, 1,
484 kMaxMicLevel, 50);
Alex Loiko99f1e0d2018-07-19 16:39:39 +0200485 // Reset the AGC since the level has changed.
486 agc_->Reset();
henrik.lundin3edc7f02016-11-24 01:42:46 -0800487 }
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000488}
489
490void AgcManagerDirect::UpdateCompressor() {
Alex Loikof3122e02018-08-10 14:43:51 +0200491 calls_since_last_gain_log_++;
492 if (calls_since_last_gain_log_ == 100) {
493 calls_since_last_gain_log_ = 0;
494 RTC_HISTOGRAM_COUNTS_LINEAR("WebRTC.Audio.Agc.DigitalGainApplied",
495 compression_, 0, kMaxCompressionGain,
496 kMaxCompressionGain + 1);
497 }
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000498 if (compression_ == target_compression_) {
499 return;
500 }
501
502 // Adapt the compression gain slowly towards the target, in order to avoid
503 // highly perceptible changes.
504 if (target_compression_ > compression_) {
505 compression_accumulator_ += kCompressionGainStep;
506 } else {
507 compression_accumulator_ -= kCompressionGainStep;
508 }
509
510 // The compressor accepts integer gains in dB. Adjust the gain when
511 // we've come within half a stepsize of the nearest integer. (We don't
512 // check for equality due to potential floating point imprecision).
513 int new_compression = compression_;
514 int nearest_neighbor = std::floor(compression_accumulator_ + 0.5);
515 if (std::fabs(compression_accumulator_ - nearest_neighbor) <
516 kCompressionGainStep / 2) {
517 new_compression = nearest_neighbor;
518 }
519
520 // Set the new compression gain.
521 if (new_compression != compression_) {
Alex Loikof3122e02018-08-10 14:43:51 +0200522 RTC_HISTOGRAM_COUNTS_LINEAR("WebRTC.Audio.Agc.DigitalGainUpdated",
523 new_compression, 0, kMaxCompressionGain,
524 kMaxCompressionGain + 1);
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000525 compression_ = new_compression;
526 compression_accumulator_ = new_compression;
527 if (gctrl_->set_compression_gain_db(compression_) != 0) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100528 RTC_LOG(LS_ERROR) << "set_compression_gain_db(" << compression_
529 << ") failed.";
pbos@webrtc.org788acd12014-12-15 09:41:24 +0000530 }
531 }
532}
533
534} // namespace webrtc