pbos@webrtc.org | 788acd1 | 2014-12-15 09:41:24 +0000 | [diff] [blame] | 1 | /* |
| 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 Bonadei | 92ea95e | 2017-09-15 06:47:31 +0200 | [diff] [blame] | 11 | #include "modules/audio_processing/agc/agc_manager_direct.h" |
pbos@webrtc.org | 788acd1 | 2014-12-15 09:41:24 +0000 | [diff] [blame] | 12 | |
Yves Gerey | 988cc08 | 2018-10-23 12:03:01 +0200 | [diff] [blame] | 13 | #include <algorithm> |
pbos@webrtc.org | 788acd1 | 2014-12-15 09:41:24 +0000 | [diff] [blame] | 14 | #include <cmath> |
| 15 | |
Per Åhgren | 928146f | 2019-08-20 09:19:21 +0200 | [diff] [blame] | 16 | #include "common_audio/include/audio_util.h" |
Sam Zackrisson | 41478c7 | 2019-10-15 10:10:26 +0200 | [diff] [blame] | 17 | #include "modules/audio_processing/agc/gain_control.h" |
Mirko Bonadei | 92ea95e | 2017-09-15 06:47:31 +0200 | [diff] [blame] | 18 | #include "modules/audio_processing/agc/gain_map_internal.h" |
Hanna Silen | a004715 | 2021-06-03 03:29:38 +0200 | [diff] [blame] | 19 | #include "modules/audio_processing/include/audio_frame_view.h" |
Per Åhgren | 3daedb6 | 2019-11-22 12:11:40 +0100 | [diff] [blame] | 20 | #include "rtc_base/atomic_ops.h" |
Mirko Bonadei | 92ea95e | 2017-09-15 06:47:31 +0200 | [diff] [blame] | 21 | #include "rtc_base/checks.h" |
| 22 | #include "rtc_base/logging.h" |
Karl Wiberg | e40468b | 2017-11-22 10:42:26 +0100 | [diff] [blame] | 23 | #include "rtc_base/numerics/safe_minmax.h" |
henrika | ebf4552 | 2019-11-04 13:59:21 +0100 | [diff] [blame] | 24 | #include "system_wrappers/include/field_trial.h" |
Mirko Bonadei | 92ea95e | 2017-09-15 06:47:31 +0200 | [diff] [blame] | 25 | #include "system_wrappers/include/metrics.h" |
pbos@webrtc.org | 788acd1 | 2014-12-15 09:41:24 +0000 | [diff] [blame] | 26 | |
| 27 | namespace webrtc { |
| 28 | |
| 29 | namespace { |
| 30 | |
pbos@webrtc.org | 788acd1 | 2014-12-15 09:41:24 +0000 | [diff] [blame] | 31 | // Amount of error we tolerate in the microphone level (presumably due to OS |
| 32 | // quantization) before we assume the user has manually adjusted the microphone. |
Hanna Silen | b8dc7fa | 2021-05-20 17:37:56 +0200 | [diff] [blame] | 33 | constexpr int kLevelQuantizationSlack = 25; |
pbos@webrtc.org | 788acd1 | 2014-12-15 09:41:24 +0000 | [diff] [blame] | 34 | |
Hanna Silen | b8dc7fa | 2021-05-20 17:37:56 +0200 | [diff] [blame] | 35 | constexpr int kDefaultCompressionGain = 7; |
| 36 | constexpr int kMaxCompressionGain = 12; |
| 37 | constexpr int kMinCompressionGain = 2; |
pbos@webrtc.org | 788acd1 | 2014-12-15 09:41:24 +0000 | [diff] [blame] | 38 | // Controls the rate of compression changes towards the target. |
Hanna Silen | b8dc7fa | 2021-05-20 17:37:56 +0200 | [diff] [blame] | 39 | constexpr float kCompressionGainStep = 0.05f; |
pbos@webrtc.org | 788acd1 | 2014-12-15 09:41:24 +0000 | [diff] [blame] | 40 | |
Hanna Silen | b8dc7fa | 2021-05-20 17:37:56 +0200 | [diff] [blame] | 41 | constexpr int kMaxMicLevel = 255; |
kwiberg@webrtc.org | 2ebfac5 | 2015-01-14 10:51:54 +0000 | [diff] [blame] | 42 | static_assert(kGainMapSize > kMaxMicLevel, "gain map too small"); |
Hanna Silen | b8dc7fa | 2021-05-20 17:37:56 +0200 | [diff] [blame] | 43 | constexpr int kMinMicLevel = 12; |
pbos@webrtc.org | 788acd1 | 2014-12-15 09:41:24 +0000 | [diff] [blame] | 44 | |
| 45 | // Prevent very large microphone level changes. |
Hanna Silen | b8dc7fa | 2021-05-20 17:37:56 +0200 | [diff] [blame] | 46 | constexpr int kMaxResidualGainChange = 15; |
pbos@webrtc.org | 788acd1 | 2014-12-15 09:41:24 +0000 | [diff] [blame] | 47 | |
| 48 | // Maximum additional gain allowed to compensate for microphone level |
| 49 | // restrictions from clipping events. |
Hanna Silen | b8dc7fa | 2021-05-20 17:37:56 +0200 | [diff] [blame] | 50 | constexpr int kSurplusCompressionGain = 6; |
pbos@webrtc.org | 788acd1 | 2014-12-15 09:41:24 +0000 | [diff] [blame] | 51 | |
Alessio Bazzica | 42dacda | 2021-06-17 17:18:46 +0200 | [diff] [blame] | 52 | // History size for the clipping predictor evaluator (unit: number of 10 ms |
| 53 | // frames). |
| 54 | constexpr int kClippingPredictorEvaluatorHistorySize = 32; |
| 55 | |
Hanna Silen | a004715 | 2021-06-03 03:29:38 +0200 | [diff] [blame] | 56 | using ClippingPredictorConfig = AudioProcessing::Config::GainController1:: |
| 57 | AnalogGainController::ClippingPredictor; |
| 58 | |
Per Åhgren | 26cc5e6 | 2019-11-26 22:58:53 +0100 | [diff] [blame] | 59 | // Returns whether a fall-back solution to choose the maximum level should be |
| 60 | // chosen. |
| 61 | bool UseMaxAnalogChannelLevel() { |
| 62 | return field_trial::IsEnabled("WebRTC-UseMaxAnalogAgcChannelLevel"); |
| 63 | } |
| 64 | |
henrika | ebf4552 | 2019-11-04 13:59:21 +0100 | [diff] [blame] | 65 | // Returns kMinMicLevel if no field trial exists or if it has been disabled. |
| 66 | // Returns a value between 0 and 255 depending on the field-trial string. |
| 67 | // Example: 'WebRTC-Audio-AgcMinMicLevelExperiment/Enabled-80' => returns 80. |
| 68 | int GetMinMicLevel() { |
| 69 | RTC_LOG(LS_INFO) << "[agc] GetMinMicLevel"; |
| 70 | constexpr char kMinMicLevelFieldTrial[] = |
| 71 | "WebRTC-Audio-AgcMinMicLevelExperiment"; |
| 72 | if (!webrtc::field_trial::IsEnabled(kMinMicLevelFieldTrial)) { |
| 73 | RTC_LOG(LS_INFO) << "[agc] Using default min mic level: " << kMinMicLevel; |
| 74 | return kMinMicLevel; |
| 75 | } |
| 76 | const auto field_trial_string = |
| 77 | webrtc::field_trial::FindFullName(kMinMicLevelFieldTrial); |
| 78 | int min_mic_level = -1; |
| 79 | sscanf(field_trial_string.c_str(), "Enabled-%d", &min_mic_level); |
| 80 | if (min_mic_level >= 0 && min_mic_level <= 255) { |
| 81 | RTC_LOG(LS_INFO) << "[agc] Experimental min mic level: " << min_mic_level; |
| 82 | return min_mic_level; |
| 83 | } else { |
| 84 | RTC_LOG(LS_WARNING) << "[agc] Invalid parameter for " |
| 85 | << kMinMicLevelFieldTrial << ", ignored."; |
| 86 | return kMinMicLevel; |
| 87 | } |
Bjorn Volcker | adc46c4 | 2015-04-15 11:42:40 +0200 | [diff] [blame] | 88 | } |
| 89 | |
henrika | ebf4552 | 2019-11-04 13:59:21 +0100 | [diff] [blame] | 90 | int ClampLevel(int mic_level, int min_mic_level) { |
| 91 | return rtc::SafeClamp(mic_level, min_mic_level, kMaxMicLevel); |
| 92 | } |
| 93 | |
| 94 | int LevelFromGainError(int gain_error, int level, int min_mic_level) { |
kwiberg | 9e2be5f | 2016-09-14 05:23:22 -0700 | [diff] [blame] | 95 | RTC_DCHECK_GE(level, 0); |
| 96 | RTC_DCHECK_LE(level, kMaxMicLevel); |
pbos@webrtc.org | 788acd1 | 2014-12-15 09:41:24 +0000 | [diff] [blame] | 97 | if (gain_error == 0) { |
| 98 | return level; |
| 99 | } |
henrika | ebf4552 | 2019-11-04 13:59:21 +0100 | [diff] [blame] | 100 | |
pbos@webrtc.org | 788acd1 | 2014-12-15 09:41:24 +0000 | [diff] [blame] | 101 | int new_level = level; |
| 102 | if (gain_error > 0) { |
| 103 | while (kGainMap[new_level] - kGainMap[level] < gain_error && |
Yves Gerey | 665174f | 2018-06-19 15:03:05 +0200 | [diff] [blame] | 104 | new_level < kMaxMicLevel) { |
pbos@webrtc.org | 788acd1 | 2014-12-15 09:41:24 +0000 | [diff] [blame] | 105 | ++new_level; |
| 106 | } |
| 107 | } else { |
| 108 | while (kGainMap[new_level] - kGainMap[level] > gain_error && |
henrika | ebf4552 | 2019-11-04 13:59:21 +0100 | [diff] [blame] | 109 | new_level > min_mic_level) { |
pbos@webrtc.org | 788acd1 | 2014-12-15 09:41:24 +0000 | [diff] [blame] | 110 | --new_level; |
| 111 | } |
| 112 | } |
| 113 | return new_level; |
| 114 | } |
| 115 | |
Per Åhgren | 361d1c3 | 2019-11-06 22:17:14 +0100 | [diff] [blame] | 116 | // Returns the proportion of samples in the buffer which are at full-scale |
| 117 | // (and presumably clipped). |
| 118 | float ComputeClippedRatio(const float* const* audio, |
| 119 | size_t num_channels, |
| 120 | size_t samples_per_channel) { |
Per Åhgren | b49aec5 | 2019-11-07 08:41:20 +0100 | [diff] [blame] | 121 | RTC_DCHECK_GT(samples_per_channel, 0); |
Per Åhgren | 361d1c3 | 2019-11-06 22:17:14 +0100 | [diff] [blame] | 122 | int num_clipped = 0; |
| 123 | for (size_t ch = 0; ch < num_channels; ++ch) { |
Per Åhgren | b49aec5 | 2019-11-07 08:41:20 +0100 | [diff] [blame] | 124 | int num_clipped_in_ch = 0; |
Per Åhgren | 361d1c3 | 2019-11-06 22:17:14 +0100 | [diff] [blame] | 125 | for (size_t i = 0; i < samples_per_channel; ++i) { |
| 126 | RTC_DCHECK(audio[ch]); |
| 127 | if (audio[ch][i] >= 32767.f || audio[ch][i] <= -32768.f) { |
Per Åhgren | b49aec5 | 2019-11-07 08:41:20 +0100 | [diff] [blame] | 128 | ++num_clipped_in_ch; |
Per Åhgren | 361d1c3 | 2019-11-06 22:17:14 +0100 | [diff] [blame] | 129 | } |
| 130 | } |
Per Åhgren | b49aec5 | 2019-11-07 08:41:20 +0100 | [diff] [blame] | 131 | num_clipped = std::max(num_clipped, num_clipped_in_ch); |
Per Åhgren | 361d1c3 | 2019-11-06 22:17:14 +0100 | [diff] [blame] | 132 | } |
Per Åhgren | b49aec5 | 2019-11-07 08:41:20 +0100 | [diff] [blame] | 133 | return static_cast<float>(num_clipped) / (samples_per_channel); |
Per Åhgren | 361d1c3 | 2019-11-06 22:17:14 +0100 | [diff] [blame] | 134 | } |
| 135 | |
Alessio Bazzica | 42dacda | 2021-06-17 17:18:46 +0200 | [diff] [blame] | 136 | void LogClippingPredictorMetrics(const ClippingPredictorEvaluator& evaluator) { |
| 137 | RTC_LOG(LS_INFO) << "Clipping predictor metrics: TP " |
| 138 | << evaluator.true_positives() << " TN " |
| 139 | << evaluator.true_negatives() << " FP " |
| 140 | << evaluator.false_positives() << " FN " |
| 141 | << evaluator.false_negatives(); |
| 142 | const float precision_denominator = |
| 143 | evaluator.true_positives() + evaluator.false_positives(); |
| 144 | const float recall_denominator = |
| 145 | evaluator.true_positives() + evaluator.false_negatives(); |
| 146 | if (precision_denominator > 0 && recall_denominator > 0) { |
| 147 | const float precision = evaluator.true_positives() / precision_denominator; |
| 148 | const float recall = evaluator.true_positives() / recall_denominator; |
| 149 | RTC_LOG(LS_INFO) << "Clipping predictor metrics: P " << precision << " R " |
| 150 | << recall; |
| 151 | const float f1_score_denominator = precision + recall; |
| 152 | if (f1_score_denominator > 0.0f) { |
| 153 | const float f1_score = 2 * precision * recall / f1_score_denominator; |
| 154 | RTC_LOG(LS_INFO) << "Clipping predictor metrics: F1 " << f1_score; |
| 155 | RTC_HISTOGRAM_COUNTS_LINEAR("WebRTC.Audio.Agc.ClippingPredictor.F1Score", |
| 156 | std::round(f1_score * 100.0f), /*min=*/0, |
| 157 | /*max=*/100, |
| 158 | /*bucket_count=*/50); |
| 159 | } |
| 160 | } |
| 161 | } |
| 162 | |
Hanna Silen | e7e9292 | 2021-07-08 17:26:31 +0200 | [diff] [blame] | 163 | void LogClippingMetrics(int clipping_rate) { |
| 164 | RTC_LOG(LS_INFO) << "Input clipping rate: " << clipping_rate << "%"; |
| 165 | RTC_HISTOGRAM_COUNTS_LINEAR("WebRTC.Audio.Agc.InputClippingRate", |
| 166 | clipping_rate, /*min=*/0, /*max=*/100, |
| 167 | /*bucket_count=*/50); |
| 168 | } |
| 169 | |
pbos@webrtc.org | 788acd1 | 2014-12-15 09:41:24 +0000 | [diff] [blame] | 170 | } // namespace |
| 171 | |
Per Åhgren | 3daedb6 | 2019-11-22 12:11:40 +0100 | [diff] [blame] | 172 | MonoAgc::MonoAgc(ApmDataDumper* data_dumper, |
| 173 | int startup_min_level, |
| 174 | int clipped_level_min, |
Per Åhgren | 3daedb6 | 2019-11-22 12:11:40 +0100 | [diff] [blame] | 175 | bool disable_digital_adaptive, |
| 176 | int min_mic_level) |
| 177 | : min_mic_level_(min_mic_level), |
| 178 | disable_digital_adaptive_(disable_digital_adaptive), |
Alessio Bazzica | 42eef86 | 2021-01-15 16:41:48 +0100 | [diff] [blame] | 179 | agc_(std::make_unique<Agc>()), |
pbos@webrtc.org | 788acd1 | 2014-12-15 09:41:24 +0000 | [diff] [blame] | 180 | max_level_(kMaxMicLevel), |
| 181 | max_compression_gain_(kMaxCompressionGain), |
| 182 | target_compression_(kDefaultCompressionGain), |
| 183 | compression_(target_compression_), |
| 184 | compression_accumulator_(compression_), |
henrika | ebf4552 | 2019-11-04 13:59:21 +0100 | [diff] [blame] | 185 | startup_min_level_(ClampLevel(startup_min_level, min_mic_level_)), |
Alessio Bazzica | 42eef86 | 2021-01-15 16:41:48 +0100 | [diff] [blame] | 186 | clipped_level_min_(clipped_level_min) {} |
pbos@webrtc.org | 788acd1 | 2014-12-15 09:41:24 +0000 | [diff] [blame] | 187 | |
Per Åhgren | 3daedb6 | 2019-11-22 12:11:40 +0100 | [diff] [blame] | 188 | MonoAgc::~MonoAgc() = default; |
pbos@webrtc.org | 788acd1 | 2014-12-15 09:41:24 +0000 | [diff] [blame] | 189 | |
Per Åhgren | 3daedb6 | 2019-11-22 12:11:40 +0100 | [diff] [blame] | 190 | void MonoAgc::Initialize() { |
pbos@webrtc.org | 788acd1 | 2014-12-15 09:41:24 +0000 | [diff] [blame] | 191 | max_level_ = kMaxMicLevel; |
| 192 | max_compression_gain_ = kMaxCompressionGain; |
Alex Loiko | 9489c3a | 2018-08-09 15:04:24 +0200 | [diff] [blame] | 193 | target_compression_ = disable_digital_adaptive_ ? 0 : kDefaultCompressionGain; |
| 194 | compression_ = disable_digital_adaptive_ ? 0 : target_compression_; |
pbos@webrtc.org | 788acd1 | 2014-12-15 09:41:24 +0000 | [diff] [blame] | 195 | compression_accumulator_ = compression_; |
Per Åhgren | 0a144a7 | 2021-02-09 08:47:51 +0100 | [diff] [blame] | 196 | capture_output_used_ = true; |
pbos@webrtc.org | 788acd1 | 2014-12-15 09:41:24 +0000 | [diff] [blame] | 197 | check_volume_on_next_process_ = true; |
Per Åhgren | 0e3198e | 2019-11-18 08:52:22 +0100 | [diff] [blame] | 198 | } |
Alex Loiko | c167673 | 2018-07-02 12:05:28 +0200 | [diff] [blame] | 199 | |
Per Åhgren | 3daedb6 | 2019-11-22 12:11:40 +0100 | [diff] [blame] | 200 | void MonoAgc::Process(const int16_t* audio, |
| 201 | size_t samples_per_channel, |
| 202 | int sample_rate_hz) { |
| 203 | new_compression_to_set_ = absl::nullopt; |
Per Åhgren | 928146f | 2019-08-20 09:19:21 +0200 | [diff] [blame] | 204 | |
pbos@webrtc.org | 788acd1 | 2014-12-15 09:41:24 +0000 | [diff] [blame] | 205 | if (check_volume_on_next_process_) { |
| 206 | check_volume_on_next_process_ = false; |
| 207 | // We have to wait until the first process call to check the volume, |
| 208 | // because Chromium doesn't guarantee it to be valid any earlier. |
| 209 | CheckVolumeAndReset(); |
| 210 | } |
| 211 | |
Per Åhgren | 3daedb6 | 2019-11-22 12:11:40 +0100 | [diff] [blame] | 212 | agc_->Process(audio, samples_per_channel, sample_rate_hz); |
pbos@webrtc.org | 788acd1 | 2014-12-15 09:41:24 +0000 | [diff] [blame] | 213 | |
| 214 | UpdateGain(); |
Alex Loiko | 9489c3a | 2018-08-09 15:04:24 +0200 | [diff] [blame] | 215 | if (!disable_digital_adaptive_) { |
| 216 | UpdateCompressor(); |
| 217 | } |
pbos@webrtc.org | 788acd1 | 2014-12-15 09:41:24 +0000 | [diff] [blame] | 218 | } |
| 219 | |
Hanna Silen | b8dc7fa | 2021-05-20 17:37:56 +0200 | [diff] [blame] | 220 | void MonoAgc::HandleClipping(int clipped_level_step) { |
Per Åhgren | 3daedb6 | 2019-11-22 12:11:40 +0100 | [diff] [blame] | 221 | // Always decrease the maximum level, even if the current level is below |
| 222 | // threshold. |
Hanna Silen | b8dc7fa | 2021-05-20 17:37:56 +0200 | [diff] [blame] | 223 | SetMaxLevel(std::max(clipped_level_min_, max_level_ - clipped_level_step)); |
Per Åhgren | 3daedb6 | 2019-11-22 12:11:40 +0100 | [diff] [blame] | 224 | if (log_to_histograms_) { |
| 225 | RTC_HISTOGRAM_BOOLEAN("WebRTC.Audio.AgcClippingAdjustmentAllowed", |
Hanna Silen | b8dc7fa | 2021-05-20 17:37:56 +0200 | [diff] [blame] | 226 | level_ - clipped_level_step >= clipped_level_min_); |
Per Åhgren | 3daedb6 | 2019-11-22 12:11:40 +0100 | [diff] [blame] | 227 | } |
| 228 | if (level_ > clipped_level_min_) { |
| 229 | // Don't try to adjust the level if we're already below the limit. As |
| 230 | // a consequence, if the user has brought the level above the limit, we |
| 231 | // will still not react until the postproc updates the level. |
Hanna Silen | b8dc7fa | 2021-05-20 17:37:56 +0200 | [diff] [blame] | 232 | SetLevel(std::max(clipped_level_min_, level_ - clipped_level_step)); |
Per Åhgren | 3daedb6 | 2019-11-22 12:11:40 +0100 | [diff] [blame] | 233 | // Reset the AGCs for all channels since the level has changed. |
| 234 | agc_->Reset(); |
| 235 | } |
| 236 | } |
| 237 | |
| 238 | void MonoAgc::SetLevel(int new_level) { |
Per Åhgren | 0e3198e | 2019-11-18 08:52:22 +0100 | [diff] [blame] | 239 | int voe_level = stream_analog_level_; |
pbos@webrtc.org | 788acd1 | 2014-12-15 09:41:24 +0000 | [diff] [blame] | 240 | if (voe_level == 0) { |
Jonas Olsson | 645b027 | 2018-02-15 15:16:27 +0100 | [diff] [blame] | 241 | RTC_DLOG(LS_INFO) |
Mirko Bonadei | 675513b | 2017-11-09 11:09:25 +0100 | [diff] [blame] | 242 | << "[agc] VolumeCallbacks returned level=0, taking no action."; |
pbos@webrtc.org | 788acd1 | 2014-12-15 09:41:24 +0000 | [diff] [blame] | 243 | return; |
| 244 | } |
Jonas Olsson | 645b027 | 2018-02-15 15:16:27 +0100 | [diff] [blame] | 245 | if (voe_level < 0 || voe_level > kMaxMicLevel) { |
Mirko Bonadei | 675513b | 2017-11-09 11:09:25 +0100 | [diff] [blame] | 246 | RTC_LOG(LS_ERROR) << "VolumeCallbacks returned an invalid level=" |
| 247 | << voe_level; |
pbos@webrtc.org | 788acd1 | 2014-12-15 09:41:24 +0000 | [diff] [blame] | 248 | return; |
| 249 | } |
| 250 | |
| 251 | if (voe_level > level_ + kLevelQuantizationSlack || |
| 252 | voe_level < level_ - kLevelQuantizationSlack) { |
Jonas Olsson | 645b027 | 2018-02-15 15:16:27 +0100 | [diff] [blame] | 253 | RTC_DLOG(LS_INFO) << "[agc] Mic volume was manually adjusted. Updating " |
Yves Gerey | 665174f | 2018-06-19 15:03:05 +0200 | [diff] [blame] | 254 | "stored level from " |
| 255 | << level_ << " to " << voe_level; |
pbos@webrtc.org | 788acd1 | 2014-12-15 09:41:24 +0000 | [diff] [blame] | 256 | level_ = voe_level; |
| 257 | // Always allow the user to increase the volume. |
| 258 | if (level_ > max_level_) { |
| 259 | SetMaxLevel(level_); |
| 260 | } |
| 261 | // Take no action in this case, since we can't be sure when the volume |
| 262 | // was manually adjusted. The compressor will still provide some of the |
| 263 | // desired gain change. |
| 264 | agc_->Reset(); |
Per Åhgren | 3daedb6 | 2019-11-22 12:11:40 +0100 | [diff] [blame] | 265 | |
pbos@webrtc.org | 788acd1 | 2014-12-15 09:41:24 +0000 | [diff] [blame] | 266 | return; |
| 267 | } |
| 268 | |
| 269 | new_level = std::min(new_level, max_level_); |
| 270 | if (new_level == level_) { |
| 271 | return; |
| 272 | } |
| 273 | |
Per Åhgren | 0e3198e | 2019-11-18 08:52:22 +0100 | [diff] [blame] | 274 | stream_analog_level_ = new_level; |
Jonas Olsson | 6c9bc39 | 2020-01-14 15:54:35 +0100 | [diff] [blame] | 275 | RTC_DLOG(LS_INFO) << "[agc] voe_level=" << voe_level << ", level_=" << level_ |
| 276 | << ", new_level=" << new_level; |
pbos@webrtc.org | 788acd1 | 2014-12-15 09:41:24 +0000 | [diff] [blame] | 277 | level_ = new_level; |
| 278 | } |
| 279 | |
Per Åhgren | 3daedb6 | 2019-11-22 12:11:40 +0100 | [diff] [blame] | 280 | void MonoAgc::SetMaxLevel(int level) { |
henrik.lundin | bd681b9 | 2016-12-05 09:08:42 -0800 | [diff] [blame] | 281 | RTC_DCHECK_GE(level, clipped_level_min_); |
pbos@webrtc.org | 788acd1 | 2014-12-15 09:41:24 +0000 | [diff] [blame] | 282 | max_level_ = level; |
| 283 | // Scale the |kSurplusCompressionGain| linearly across the restricted |
| 284 | // level range. |
henrik.lundin | bd681b9 | 2016-12-05 09:08:42 -0800 | [diff] [blame] | 285 | max_compression_gain_ = |
| 286 | kMaxCompressionGain + std::floor((1.f * kMaxMicLevel - max_level_) / |
| 287 | (kMaxMicLevel - clipped_level_min_) * |
| 288 | kSurplusCompressionGain + |
| 289 | 0.5f); |
Jonas Olsson | 645b027 | 2018-02-15 15:16:27 +0100 | [diff] [blame] | 290 | RTC_DLOG(LS_INFO) << "[agc] max_level_=" << max_level_ |
| 291 | << ", max_compression_gain_=" << max_compression_gain_; |
pbos@webrtc.org | 788acd1 | 2014-12-15 09:41:24 +0000 | [diff] [blame] | 292 | } |
| 293 | |
Per Åhgren | 0a144a7 | 2021-02-09 08:47:51 +0100 | [diff] [blame] | 294 | void MonoAgc::HandleCaptureOutputUsedChange(bool capture_output_used) { |
| 295 | if (capture_output_used_ == capture_output_used) { |
pbos@webrtc.org | 788acd1 | 2014-12-15 09:41:24 +0000 | [diff] [blame] | 296 | return; |
| 297 | } |
Per Åhgren | 0a144a7 | 2021-02-09 08:47:51 +0100 | [diff] [blame] | 298 | capture_output_used_ = capture_output_used; |
pbos@webrtc.org | 788acd1 | 2014-12-15 09:41:24 +0000 | [diff] [blame] | 299 | |
Per Åhgren | 0a144a7 | 2021-02-09 08:47:51 +0100 | [diff] [blame] | 300 | if (capture_output_used) { |
| 301 | // When we start using the output, we should reset things to be safe. |
pbos@webrtc.org | 788acd1 | 2014-12-15 09:41:24 +0000 | [diff] [blame] | 302 | check_volume_on_next_process_ = true; |
| 303 | } |
| 304 | } |
| 305 | |
Per Åhgren | 3daedb6 | 2019-11-22 12:11:40 +0100 | [diff] [blame] | 306 | int MonoAgc::CheckVolumeAndReset() { |
Per Åhgren | 0e3198e | 2019-11-18 08:52:22 +0100 | [diff] [blame] | 307 | int level = stream_analog_level_; |
pbos@webrtc.org | 788acd1 | 2014-12-15 09:41:24 +0000 | [diff] [blame] | 308 | // Reasons for taking action at startup: |
| 309 | // 1) A person starting a call is expected to be heard. |
| 310 | // 2) Independent of interpretation of |level| == 0 we should raise it so the |
| 311 | // AGC can do its job properly. |
| 312 | if (level == 0 && !startup_) { |
Jonas Olsson | 645b027 | 2018-02-15 15:16:27 +0100 | [diff] [blame] | 313 | RTC_DLOG(LS_INFO) |
Mirko Bonadei | 675513b | 2017-11-09 11:09:25 +0100 | [diff] [blame] | 314 | << "[agc] VolumeCallbacks returned level=0, taking no action."; |
pbos@webrtc.org | 788acd1 | 2014-12-15 09:41:24 +0000 | [diff] [blame] | 315 | return 0; |
| 316 | } |
Jonas Olsson | 645b027 | 2018-02-15 15:16:27 +0100 | [diff] [blame] | 317 | if (level < 0 || level > kMaxMicLevel) { |
| 318 | RTC_LOG(LS_ERROR) << "[agc] VolumeCallbacks returned an invalid level=" |
| 319 | << level; |
pbos@webrtc.org | 788acd1 | 2014-12-15 09:41:24 +0000 | [diff] [blame] | 320 | return -1; |
| 321 | } |
Jonas Olsson | 645b027 | 2018-02-15 15:16:27 +0100 | [diff] [blame] | 322 | RTC_DLOG(LS_INFO) << "[agc] Initial GetMicVolume()=" << level; |
pbos@webrtc.org | 788acd1 | 2014-12-15 09:41:24 +0000 | [diff] [blame] | 323 | |
henrika | ebf4552 | 2019-11-04 13:59:21 +0100 | [diff] [blame] | 324 | int minLevel = startup_ ? startup_min_level_ : min_mic_level_; |
pbos@webrtc.org | 788acd1 | 2014-12-15 09:41:24 +0000 | [diff] [blame] | 325 | if (level < minLevel) { |
| 326 | level = minLevel; |
Jonas Olsson | 645b027 | 2018-02-15 15:16:27 +0100 | [diff] [blame] | 327 | RTC_DLOG(LS_INFO) << "[agc] Initial volume too low, raising to " << level; |
Per Åhgren | 0e3198e | 2019-11-18 08:52:22 +0100 | [diff] [blame] | 328 | stream_analog_level_ = level; |
pbos@webrtc.org | 788acd1 | 2014-12-15 09:41:24 +0000 | [diff] [blame] | 329 | } |
| 330 | agc_->Reset(); |
| 331 | level_ = level; |
| 332 | startup_ = false; |
| 333 | return 0; |
| 334 | } |
| 335 | |
| 336 | // Requests the RMS error from AGC and distributes the required gain change |
| 337 | // between the digital compression stage and volume slider. We use the |
| 338 | // compressor first, providing a slack region around the current slider |
| 339 | // position to reduce movement. |
| 340 | // |
| 341 | // If the slider needs to be moved, we check first if the user has adjusted |
| 342 | // it, in which case we take no action and cache the updated level. |
Per Åhgren | 3daedb6 | 2019-11-22 12:11:40 +0100 | [diff] [blame] | 343 | void MonoAgc::UpdateGain() { |
pbos@webrtc.org | 788acd1 | 2014-12-15 09:41:24 +0000 | [diff] [blame] | 344 | int rms_error = 0; |
| 345 | if (!agc_->GetRmsErrorDb(&rms_error)) { |
| 346 | // No error update ready. |
| 347 | return; |
| 348 | } |
| 349 | // The compressor will always add at least kMinCompressionGain. In effect, |
| 350 | // this adjusts our target gain upward by the same amount and rms_error |
| 351 | // needs to reflect that. |
| 352 | rms_error += kMinCompressionGain; |
| 353 | |
| 354 | // Handle as much error as possible with the compressor first. |
kwiberg | 0703856 | 2017-06-12 11:40:47 -0700 | [diff] [blame] | 355 | int raw_compression = |
| 356 | rtc::SafeClamp(rms_error, kMinCompressionGain, max_compression_gain_); |
| 357 | |
pbos@webrtc.org | 788acd1 | 2014-12-15 09:41:24 +0000 | [diff] [blame] | 358 | // Deemphasize the compression gain error. Move halfway between the current |
| 359 | // target and the newly received target. This serves to soften perceptible |
| 360 | // intra-talkspurt adjustments, at the cost of some adaptation speed. |
| 361 | if ((raw_compression == max_compression_gain_ && |
Yves Gerey | 665174f | 2018-06-19 15:03:05 +0200 | [diff] [blame] | 362 | target_compression_ == max_compression_gain_ - 1) || |
pbos@webrtc.org | 788acd1 | 2014-12-15 09:41:24 +0000 | [diff] [blame] | 363 | (raw_compression == kMinCompressionGain && |
Yves Gerey | 665174f | 2018-06-19 15:03:05 +0200 | [diff] [blame] | 364 | target_compression_ == kMinCompressionGain + 1)) { |
pbos@webrtc.org | 788acd1 | 2014-12-15 09:41:24 +0000 | [diff] [blame] | 365 | // Special case to allow the target to reach the endpoints of the |
| 366 | // compression range. The deemphasis would otherwise halt it at 1 dB shy. |
| 367 | target_compression_ = raw_compression; |
| 368 | } else { |
Yves Gerey | 665174f | 2018-06-19 15:03:05 +0200 | [diff] [blame] | 369 | target_compression_ = |
| 370 | (raw_compression - target_compression_) / 2 + target_compression_; |
pbos@webrtc.org | 788acd1 | 2014-12-15 09:41:24 +0000 | [diff] [blame] | 371 | } |
| 372 | |
| 373 | // Residual error will be handled by adjusting the volume slider. Use the |
| 374 | // raw rather than deemphasized compression here as we would otherwise |
| 375 | // shrink the amount of slack the compressor provides. |
kwiberg | 0703856 | 2017-06-12 11:40:47 -0700 | [diff] [blame] | 376 | const int residual_gain = |
| 377 | rtc::SafeClamp(rms_error - raw_compression, -kMaxResidualGainChange, |
| 378 | kMaxResidualGainChange); |
Jonas Olsson | 645b027 | 2018-02-15 15:16:27 +0100 | [diff] [blame] | 379 | RTC_DLOG(LS_INFO) << "[agc] rms_error=" << rms_error |
| 380 | << ", target_compression=" << target_compression_ |
| 381 | << ", residual_gain=" << residual_gain; |
pbos@webrtc.org | 788acd1 | 2014-12-15 09:41:24 +0000 | [diff] [blame] | 382 | if (residual_gain == 0) |
| 383 | return; |
| 384 | |
henrik.lundin | 3edc7f0 | 2016-11-24 01:42:46 -0800 | [diff] [blame] | 385 | int old_level = level_; |
henrika | ebf4552 | 2019-11-04 13:59:21 +0100 | [diff] [blame] | 386 | SetLevel(LevelFromGainError(residual_gain, level_, min_mic_level_)); |
henrik.lundin | 3edc7f0 | 2016-11-24 01:42:46 -0800 | [diff] [blame] | 387 | if (old_level != level_) { |
| 388 | // level_ was updated by SetLevel; log the new value. |
henrik.lundin | 45bb513 | 2016-12-06 04:28:04 -0800 | [diff] [blame] | 389 | RTC_HISTOGRAM_COUNTS_LINEAR("WebRTC.Audio.AgcSetLevel", level_, 1, |
| 390 | kMaxMicLevel, 50); |
Alex Loiko | 99f1e0d | 2018-07-19 16:39:39 +0200 | [diff] [blame] | 391 | // Reset the AGC since the level has changed. |
| 392 | agc_->Reset(); |
henrik.lundin | 3edc7f0 | 2016-11-24 01:42:46 -0800 | [diff] [blame] | 393 | } |
pbos@webrtc.org | 788acd1 | 2014-12-15 09:41:24 +0000 | [diff] [blame] | 394 | } |
| 395 | |
Per Åhgren | 3daedb6 | 2019-11-22 12:11:40 +0100 | [diff] [blame] | 396 | void MonoAgc::UpdateCompressor() { |
Alex Loiko | f3122e0 | 2018-08-10 14:43:51 +0200 | [diff] [blame] | 397 | calls_since_last_gain_log_++; |
| 398 | if (calls_since_last_gain_log_ == 100) { |
| 399 | calls_since_last_gain_log_ = 0; |
| 400 | RTC_HISTOGRAM_COUNTS_LINEAR("WebRTC.Audio.Agc.DigitalGainApplied", |
| 401 | compression_, 0, kMaxCompressionGain, |
| 402 | kMaxCompressionGain + 1); |
| 403 | } |
pbos@webrtc.org | 788acd1 | 2014-12-15 09:41:24 +0000 | [diff] [blame] | 404 | if (compression_ == target_compression_) { |
| 405 | return; |
| 406 | } |
| 407 | |
| 408 | // Adapt the compression gain slowly towards the target, in order to avoid |
| 409 | // highly perceptible changes. |
| 410 | if (target_compression_ > compression_) { |
| 411 | compression_accumulator_ += kCompressionGainStep; |
| 412 | } else { |
| 413 | compression_accumulator_ -= kCompressionGainStep; |
| 414 | } |
| 415 | |
| 416 | // The compressor accepts integer gains in dB. Adjust the gain when |
| 417 | // we've come within half a stepsize of the nearest integer. (We don't |
| 418 | // check for equality due to potential floating point imprecision). |
| 419 | int new_compression = compression_; |
| 420 | int nearest_neighbor = std::floor(compression_accumulator_ + 0.5); |
| 421 | if (std::fabs(compression_accumulator_ - nearest_neighbor) < |
| 422 | kCompressionGainStep / 2) { |
| 423 | new_compression = nearest_neighbor; |
| 424 | } |
| 425 | |
| 426 | // Set the new compression gain. |
| 427 | if (new_compression != compression_) { |
Alex Loiko | f3122e0 | 2018-08-10 14:43:51 +0200 | [diff] [blame] | 428 | RTC_HISTOGRAM_COUNTS_LINEAR("WebRTC.Audio.Agc.DigitalGainUpdated", |
| 429 | new_compression, 0, kMaxCompressionGain, |
| 430 | kMaxCompressionGain + 1); |
pbos@webrtc.org | 788acd1 | 2014-12-15 09:41:24 +0000 | [diff] [blame] | 431 | compression_ = new_compression; |
| 432 | compression_accumulator_ = new_compression; |
Per Åhgren | 0e3198e | 2019-11-18 08:52:22 +0100 | [diff] [blame] | 433 | new_compression_to_set_ = compression_; |
pbos@webrtc.org | 788acd1 | 2014-12-15 09:41:24 +0000 | [diff] [blame] | 434 | } |
| 435 | } |
| 436 | |
Per Åhgren | 3daedb6 | 2019-11-22 12:11:40 +0100 | [diff] [blame] | 437 | int AgcManagerDirect::instance_counter_ = 0; |
| 438 | |
Alessio Bazzica | 42dacda | 2021-06-17 17:18:46 +0200 | [diff] [blame] | 439 | AgcManagerDirect::AgcManagerDirect( |
| 440 | Agc* agc, |
| 441 | int startup_min_level, |
| 442 | int clipped_level_min, |
| 443 | int sample_rate_hz, |
| 444 | int clipped_level_step, |
| 445 | float clipped_ratio_threshold, |
| 446 | int clipped_wait_frames, |
| 447 | const ClippingPredictorConfig& clipping_config) |
Per Åhgren | 3daedb6 | 2019-11-22 12:11:40 +0100 | [diff] [blame] | 448 | : AgcManagerDirect(/*num_capture_channels*/ 1, |
| 449 | startup_min_level, |
| 450 | clipped_level_min, |
Per Åhgren | 3daedb6 | 2019-11-22 12:11:40 +0100 | [diff] [blame] | 451 | /*disable_digital_adaptive*/ false, |
Hanna Silen | b8dc7fa | 2021-05-20 17:37:56 +0200 | [diff] [blame] | 452 | sample_rate_hz, |
| 453 | clipped_level_step, |
| 454 | clipped_ratio_threshold, |
Hanna Silen | a004715 | 2021-06-03 03:29:38 +0200 | [diff] [blame] | 455 | clipped_wait_frames, |
Alessio Bazzica | 42dacda | 2021-06-17 17:18:46 +0200 | [diff] [blame] | 456 | clipping_config) { |
Per Åhgren | 3daedb6 | 2019-11-22 12:11:40 +0100 | [diff] [blame] | 457 | RTC_DCHECK(channel_agcs_[0]); |
| 458 | RTC_DCHECK(agc); |
| 459 | channel_agcs_[0]->set_agc(agc); |
| 460 | } |
| 461 | |
Alessio Bazzica | 42dacda | 2021-06-17 17:18:46 +0200 | [diff] [blame] | 462 | AgcManagerDirect::AgcManagerDirect( |
| 463 | int num_capture_channels, |
| 464 | int startup_min_level, |
| 465 | int clipped_level_min, |
| 466 | bool disable_digital_adaptive, |
| 467 | int sample_rate_hz, |
| 468 | int clipped_level_step, |
| 469 | float clipped_ratio_threshold, |
| 470 | int clipped_wait_frames, |
| 471 | const ClippingPredictorConfig& clipping_config) |
Per Åhgren | 3daedb6 | 2019-11-22 12:11:40 +0100 | [diff] [blame] | 472 | : data_dumper_( |
| 473 | new ApmDataDumper(rtc::AtomicOps::Increment(&instance_counter_))), |
Per Åhgren | 26cc5e6 | 2019-11-26 22:58:53 +0100 | [diff] [blame] | 474 | use_min_channel_level_(!UseMaxAnalogChannelLevel()), |
Per Åhgren | 3daedb6 | 2019-11-22 12:11:40 +0100 | [diff] [blame] | 475 | sample_rate_hz_(sample_rate_hz), |
| 476 | num_capture_channels_(num_capture_channels), |
| 477 | disable_digital_adaptive_(disable_digital_adaptive), |
Hanna Silen | b8dc7fa | 2021-05-20 17:37:56 +0200 | [diff] [blame] | 478 | frames_since_clipped_(clipped_wait_frames), |
Per Åhgren | 0a144a7 | 2021-02-09 08:47:51 +0100 | [diff] [blame] | 479 | capture_output_used_(true), |
Hanna Silen | b8dc7fa | 2021-05-20 17:37:56 +0200 | [diff] [blame] | 480 | clipped_level_step_(clipped_level_step), |
| 481 | clipped_ratio_threshold_(clipped_ratio_threshold), |
| 482 | clipped_wait_frames_(clipped_wait_frames), |
Per Åhgren | 3daedb6 | 2019-11-22 12:11:40 +0100 | [diff] [blame] | 483 | channel_agcs_(num_capture_channels), |
Hanna Silen | a004715 | 2021-06-03 03:29:38 +0200 | [diff] [blame] | 484 | new_compressions_to_set_(num_capture_channels), |
| 485 | clipping_predictor_( |
Alessio Bazzica | 42dacda | 2021-06-17 17:18:46 +0200 | [diff] [blame] | 486 | CreateClippingPredictor(num_capture_channels, clipping_config)), |
| 487 | use_clipping_predictor_step_(!!clipping_predictor_ && |
| 488 | clipping_config.use_predicted_step), |
| 489 | clipping_predictor_evaluator_(kClippingPredictorEvaluatorHistorySize), |
Hanna Silen | e7e9292 | 2021-07-08 17:26:31 +0200 | [diff] [blame] | 490 | clipping_predictor_log_counter_(0), |
| 491 | clipping_rate_log_(0.0f), |
| 492 | clipping_rate_log_counter_(0) { |
Per Åhgren | 3daedb6 | 2019-11-22 12:11:40 +0100 | [diff] [blame] | 493 | const int min_mic_level = GetMinMicLevel(); |
| 494 | for (size_t ch = 0; ch < channel_agcs_.size(); ++ch) { |
| 495 | ApmDataDumper* data_dumper_ch = ch == 0 ? data_dumper_.get() : nullptr; |
| 496 | |
| 497 | channel_agcs_[ch] = std::make_unique<MonoAgc>( |
| 498 | data_dumper_ch, startup_min_level, clipped_level_min, |
Alessio Bazzica | 42eef86 | 2021-01-15 16:41:48 +0100 | [diff] [blame] | 499 | disable_digital_adaptive_, min_mic_level); |
Per Åhgren | 3daedb6 | 2019-11-22 12:11:40 +0100 | [diff] [blame] | 500 | } |
Hanna Silen | b8dc7fa | 2021-05-20 17:37:56 +0200 | [diff] [blame] | 501 | RTC_DCHECK(!channel_agcs_.empty()); |
| 502 | RTC_DCHECK_GT(clipped_level_step, 0); |
| 503 | RTC_DCHECK_LE(clipped_level_step, 255); |
| 504 | RTC_DCHECK_GT(clipped_ratio_threshold, 0.f); |
| 505 | RTC_DCHECK_LT(clipped_ratio_threshold, 1.f); |
| 506 | RTC_DCHECK_GT(clipped_wait_frames, 0); |
Per Åhgren | 3daedb6 | 2019-11-22 12:11:40 +0100 | [diff] [blame] | 507 | channel_agcs_[0]->ActivateLogging(); |
| 508 | } |
| 509 | |
| 510 | AgcManagerDirect::~AgcManagerDirect() {} |
| 511 | |
| 512 | void AgcManagerDirect::Initialize() { |
| 513 | RTC_DLOG(LS_INFO) << "AgcManagerDirect::Initialize"; |
| 514 | data_dumper_->InitiateNewSetOfRecordings(); |
| 515 | for (size_t ch = 0; ch < channel_agcs_.size(); ++ch) { |
| 516 | channel_agcs_[ch]->Initialize(); |
| 517 | } |
Per Åhgren | 0a144a7 | 2021-02-09 08:47:51 +0100 | [diff] [blame] | 518 | capture_output_used_ = true; |
Per Åhgren | 3daedb6 | 2019-11-22 12:11:40 +0100 | [diff] [blame] | 519 | |
| 520 | AggregateChannelLevels(); |
Alessio Bazzica | 42dacda | 2021-06-17 17:18:46 +0200 | [diff] [blame] | 521 | clipping_predictor_evaluator_.Reset(); |
| 522 | clipping_predictor_log_counter_ = 0; |
Hanna Silen | e7e9292 | 2021-07-08 17:26:31 +0200 | [diff] [blame] | 523 | clipping_rate_log_ = 0.0f; |
| 524 | clipping_rate_log_counter_ = 0; |
Per Åhgren | 3daedb6 | 2019-11-22 12:11:40 +0100 | [diff] [blame] | 525 | } |
| 526 | |
| 527 | void AgcManagerDirect::SetupDigitalGainControl( |
| 528 | GainControl* gain_control) const { |
| 529 | RTC_DCHECK(gain_control); |
| 530 | if (gain_control->set_mode(GainControl::kFixedDigital) != 0) { |
| 531 | RTC_LOG(LS_ERROR) << "set_mode(GainControl::kFixedDigital) failed."; |
| 532 | } |
| 533 | const int target_level_dbfs = disable_digital_adaptive_ ? 0 : 2; |
| 534 | if (gain_control->set_target_level_dbfs(target_level_dbfs) != 0) { |
| 535 | RTC_LOG(LS_ERROR) << "set_target_level_dbfs() failed."; |
| 536 | } |
| 537 | const int compression_gain_db = |
| 538 | disable_digital_adaptive_ ? 0 : kDefaultCompressionGain; |
| 539 | if (gain_control->set_compression_gain_db(compression_gain_db) != 0) { |
| 540 | RTC_LOG(LS_ERROR) << "set_compression_gain_db() failed."; |
| 541 | } |
| 542 | const bool enable_limiter = !disable_digital_adaptive_; |
| 543 | if (gain_control->enable_limiter(enable_limiter) != 0) { |
| 544 | RTC_LOG(LS_ERROR) << "enable_limiter() failed."; |
| 545 | } |
| 546 | } |
| 547 | |
| 548 | void AgcManagerDirect::AnalyzePreProcess(const AudioBuffer* audio) { |
| 549 | RTC_DCHECK(audio); |
| 550 | AnalyzePreProcess(audio->channels_const(), audio->num_frames()); |
| 551 | } |
| 552 | |
| 553 | void AgcManagerDirect::AnalyzePreProcess(const float* const* audio, |
| 554 | size_t samples_per_channel) { |
| 555 | RTC_DCHECK(audio); |
| 556 | AggregateChannelLevels(); |
Per Åhgren | 0a144a7 | 2021-02-09 08:47:51 +0100 | [diff] [blame] | 557 | if (!capture_output_used_) { |
Per Åhgren | 3daedb6 | 2019-11-22 12:11:40 +0100 | [diff] [blame] | 558 | return; |
| 559 | } |
| 560 | |
Hanna Silen | a004715 | 2021-06-03 03:29:38 +0200 | [diff] [blame] | 561 | if (!!clipping_predictor_) { |
| 562 | AudioFrameView<const float> frame = AudioFrameView<const float>( |
| 563 | audio, num_capture_channels_, static_cast<int>(samples_per_channel)); |
Alessio Bazzica | b237a87 | 2021-06-11 12:37:54 +0200 | [diff] [blame] | 564 | clipping_predictor_->Analyze(frame); |
Hanna Silen | a004715 | 2021-06-03 03:29:38 +0200 | [diff] [blame] | 565 | } |
| 566 | |
Per Åhgren | 3daedb6 | 2019-11-22 12:11:40 +0100 | [diff] [blame] | 567 | // Check for clipped samples, as the AGC has difficulty detecting pitch |
| 568 | // under clipping distortion. We do this in the preprocessing phase in order |
| 569 | // to catch clipped echo as well. |
| 570 | // |
| 571 | // If we find a sufficiently clipped frame, drop the current microphone level |
| 572 | // and enforce a new maximum level, dropped the same amount from the current |
| 573 | // maximum. This harsh treatment is an effort to avoid repeated clipped echo |
| 574 | // events. As compensation for this restriction, the maximum compression |
| 575 | // gain is increased, through SetMaxLevel(). |
| 576 | float clipped_ratio = |
| 577 | ComputeClippedRatio(audio, num_capture_channels_, samples_per_channel); |
Hanna Silen | e7e9292 | 2021-07-08 17:26:31 +0200 | [diff] [blame] | 578 | clipping_rate_log_ = std::max(clipped_ratio, clipping_rate_log_); |
| 579 | clipping_rate_log_counter_++; |
| 580 | constexpr int kNumFramesIn30Seconds = 3000; |
| 581 | if (clipping_rate_log_counter_ == kNumFramesIn30Seconds) { |
| 582 | LogClippingMetrics(std::round(100.0f * clipping_rate_log_)); |
| 583 | clipping_rate_log_ = 0.0f; |
| 584 | clipping_rate_log_counter_ = 0; |
| 585 | } |
| 586 | |
| 587 | if (frames_since_clipped_ < clipped_wait_frames_) { |
| 588 | ++frames_since_clipped_; |
| 589 | return; |
| 590 | } |
| 591 | |
Hanna Silen | a004715 | 2021-06-03 03:29:38 +0200 | [diff] [blame] | 592 | const bool clipping_detected = clipped_ratio > clipped_ratio_threshold_; |
| 593 | bool clipping_predicted = false; |
| 594 | int predicted_step = 0; |
| 595 | if (!!clipping_predictor_) { |
| 596 | for (int channel = 0; channel < num_capture_channels_; ++channel) { |
| 597 | const auto step = clipping_predictor_->EstimateClippedLevelStep( |
| 598 | channel, stream_analog_level_, clipped_level_step_, |
| 599 | channel_agcs_[channel]->min_mic_level(), kMaxMicLevel); |
Alessio Bazzica | 42dacda | 2021-06-17 17:18:46 +0200 | [diff] [blame] | 600 | if (use_clipping_predictor_step_ && step.has_value()) { |
Hanna Silen | a004715 | 2021-06-03 03:29:38 +0200 | [diff] [blame] | 601 | predicted_step = std::max(predicted_step, step.value()); |
| 602 | clipping_predicted = true; |
| 603 | } |
| 604 | } |
Alessio Bazzica | 42dacda | 2021-06-17 17:18:46 +0200 | [diff] [blame] | 605 | // Clipping prediction evaluation. |
| 606 | absl::optional<int> prediction_interval = |
| 607 | clipping_predictor_evaluator_.Observe(clipping_detected, |
| 608 | clipping_predicted); |
| 609 | if (prediction_interval.has_value()) { |
| 610 | RTC_HISTOGRAM_COUNTS_LINEAR( |
| 611 | "WebRTC.Audio.Agc.ClippingPredictor.PredictionInterval", |
| 612 | prediction_interval.value(), /*min=*/0, |
| 613 | /*max=*/49, /*bucket_count=*/50); |
| 614 | } |
Alessio Bazzica | 42dacda | 2021-06-17 17:18:46 +0200 | [diff] [blame] | 615 | clipping_predictor_log_counter_++; |
| 616 | if (clipping_predictor_log_counter_ == kNumFramesIn30Seconds) { |
| 617 | LogClippingPredictorMetrics(clipping_predictor_evaluator_); |
| 618 | clipping_predictor_log_counter_ = 0; |
| 619 | } |
Hanna Silen | a004715 | 2021-06-03 03:29:38 +0200 | [diff] [blame] | 620 | } |
| 621 | if (clipping_detected || clipping_predicted) { |
| 622 | int step = clipped_level_step_; |
| 623 | if (clipping_detected) { |
| 624 | RTC_DLOG(LS_INFO) << "[agc] Clipping detected. clipped_ratio=" |
| 625 | << clipped_ratio; |
| 626 | } |
| 627 | if (clipping_predicted) { |
| 628 | step = std::max(predicted_step, clipped_level_step_); |
| 629 | RTC_DLOG(LS_INFO) << "[agc] Clipping predicted. step=" << step; |
| 630 | } |
Per Åhgren | 3daedb6 | 2019-11-22 12:11:40 +0100 | [diff] [blame] | 631 | for (auto& state_ch : channel_agcs_) { |
Hanna Silen | a004715 | 2021-06-03 03:29:38 +0200 | [diff] [blame] | 632 | state_ch->HandleClipping(step); |
Per Åhgren | 3daedb6 | 2019-11-22 12:11:40 +0100 | [diff] [blame] | 633 | } |
| 634 | frames_since_clipped_ = 0; |
Hanna Silen | a004715 | 2021-06-03 03:29:38 +0200 | [diff] [blame] | 635 | if (!!clipping_predictor_) { |
| 636 | clipping_predictor_->Reset(); |
Alessio Bazzica | 42dacda | 2021-06-17 17:18:46 +0200 | [diff] [blame] | 637 | clipping_predictor_evaluator_.Reset(); |
Hanna Silen | a004715 | 2021-06-03 03:29:38 +0200 | [diff] [blame] | 638 | } |
Per Åhgren | 3daedb6 | 2019-11-22 12:11:40 +0100 | [diff] [blame] | 639 | } |
| 640 | AggregateChannelLevels(); |
| 641 | } |
| 642 | |
| 643 | void AgcManagerDirect::Process(const AudioBuffer* audio) { |
| 644 | AggregateChannelLevels(); |
| 645 | |
Per Åhgren | 0a144a7 | 2021-02-09 08:47:51 +0100 | [diff] [blame] | 646 | if (!capture_output_used_) { |
Per Åhgren | 3daedb6 | 2019-11-22 12:11:40 +0100 | [diff] [blame] | 647 | return; |
| 648 | } |
| 649 | |
| 650 | for (size_t ch = 0; ch < channel_agcs_.size(); ++ch) { |
| 651 | int16_t* audio_use = nullptr; |
| 652 | std::array<int16_t, AudioBuffer::kMaxSampleRate / 100> audio_data; |
| 653 | int num_frames_per_band; |
| 654 | if (audio) { |
| 655 | FloatS16ToS16(audio->split_bands_const_f(ch)[0], |
| 656 | audio->num_frames_per_band(), audio_data.data()); |
| 657 | audio_use = audio_data.data(); |
| 658 | num_frames_per_band = audio->num_frames_per_band(); |
| 659 | } else { |
| 660 | // Only used for testing. |
| 661 | // TODO(peah): Change unittests to only allow on non-null audio input. |
| 662 | num_frames_per_band = 320; |
| 663 | } |
| 664 | channel_agcs_[ch]->Process(audio_use, num_frames_per_band, sample_rate_hz_); |
| 665 | new_compressions_to_set_[ch] = channel_agcs_[ch]->new_compression(); |
| 666 | } |
| 667 | |
| 668 | AggregateChannelLevels(); |
| 669 | } |
| 670 | |
| 671 | absl::optional<int> AgcManagerDirect::GetDigitalComressionGain() { |
| 672 | return new_compressions_to_set_[channel_controlling_gain_]; |
| 673 | } |
| 674 | |
Per Åhgren | 0a144a7 | 2021-02-09 08:47:51 +0100 | [diff] [blame] | 675 | void AgcManagerDirect::HandleCaptureOutputUsedChange(bool capture_output_used) { |
Per Åhgren | 3daedb6 | 2019-11-22 12:11:40 +0100 | [diff] [blame] | 676 | for (size_t ch = 0; ch < channel_agcs_.size(); ++ch) { |
Per Åhgren | 0a144a7 | 2021-02-09 08:47:51 +0100 | [diff] [blame] | 677 | channel_agcs_[ch]->HandleCaptureOutputUsedChange(capture_output_used); |
Per Åhgren | 3daedb6 | 2019-11-22 12:11:40 +0100 | [diff] [blame] | 678 | } |
Per Åhgren | 0a144a7 | 2021-02-09 08:47:51 +0100 | [diff] [blame] | 679 | capture_output_used_ = capture_output_used; |
Per Åhgren | 3daedb6 | 2019-11-22 12:11:40 +0100 | [diff] [blame] | 680 | } |
| 681 | |
| 682 | float AgcManagerDirect::voice_probability() const { |
| 683 | float max_prob = 0.f; |
| 684 | for (const auto& state_ch : channel_agcs_) { |
| 685 | max_prob = std::max(max_prob, state_ch->voice_probability()); |
| 686 | } |
| 687 | |
| 688 | return max_prob; |
| 689 | } |
| 690 | |
| 691 | void AgcManagerDirect::set_stream_analog_level(int level) { |
| 692 | for (size_t ch = 0; ch < channel_agcs_.size(); ++ch) { |
| 693 | channel_agcs_[ch]->set_stream_analog_level(level); |
| 694 | } |
| 695 | |
| 696 | AggregateChannelLevels(); |
| 697 | } |
| 698 | |
| 699 | void AgcManagerDirect::AggregateChannelLevels() { |
| 700 | stream_analog_level_ = channel_agcs_[0]->stream_analog_level(); |
| 701 | channel_controlling_gain_ = 0; |
Per Åhgren | 26cc5e6 | 2019-11-26 22:58:53 +0100 | [diff] [blame] | 702 | if (use_min_channel_level_) { |
| 703 | for (size_t ch = 1; ch < channel_agcs_.size(); ++ch) { |
| 704 | int level = channel_agcs_[ch]->stream_analog_level(); |
| 705 | if (level < stream_analog_level_) { |
| 706 | stream_analog_level_ = level; |
| 707 | channel_controlling_gain_ = static_cast<int>(ch); |
| 708 | } |
| 709 | } |
| 710 | } else { |
| 711 | for (size_t ch = 1; ch < channel_agcs_.size(); ++ch) { |
| 712 | int level = channel_agcs_[ch]->stream_analog_level(); |
| 713 | if (level > stream_analog_level_) { |
| 714 | stream_analog_level_ = level; |
| 715 | channel_controlling_gain_ = static_cast<int>(ch); |
| 716 | } |
Per Åhgren | 3daedb6 | 2019-11-22 12:11:40 +0100 | [diff] [blame] | 717 | } |
| 718 | } |
| 719 | } |
| 720 | |
pbos@webrtc.org | 788acd1 | 2014-12-15 09:41:24 +0000 | [diff] [blame] | 721 | } // namespace webrtc |