1 /*
2  *  Copyright (c) 2017 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 
11 #include "modules/audio_processing/gain_controller2.h"
12 
13 #include "common_audio/include/audio_util.h"
14 #include "modules/audio_processing/audio_buffer.h"
15 #include "modules/audio_processing/include/audio_frame_view.h"
16 #include "modules/audio_processing/logging/apm_data_dumper.h"
17 #include "rtc_base/atomic_ops.h"
18 #include "rtc_base/checks.h"
19 #include "rtc_base/logging.h"
20 #include "rtc_base/strings/string_builder.h"
21 
22 namespace webrtc {
23 
24 int GainController2::instance_count_ = 0;
25 
GainController2()26 GainController2::GainController2()
27     : data_dumper_(rtc::AtomicOps::Increment(&instance_count_)),
28       gain_applier_(/*hard_clip_samples=*/false,
29                     /*initial_gain_factor=*/0.f),
30       limiter_(static_cast<size_t>(48000), &data_dumper_, "Agc2"),
31       calls_since_last_limiter_log_(0) {
32   if (config_.adaptive_digital.enabled) {
33     adaptive_agc_ = std::make_unique<AdaptiveAgc>(&data_dumper_);
34   }
35 }
36 
37 GainController2::~GainController2() = default;
38 
Initialize(int sample_rate_hz)39 void GainController2::Initialize(int sample_rate_hz) {
40   RTC_DCHECK(sample_rate_hz == AudioProcessing::kSampleRate8kHz ||
41              sample_rate_hz == AudioProcessing::kSampleRate16kHz ||
42              sample_rate_hz == AudioProcessing::kSampleRate32kHz ||
43              sample_rate_hz == AudioProcessing::kSampleRate48kHz);
44   limiter_.SetSampleRate(sample_rate_hz);
45   data_dumper_.InitiateNewSetOfRecordings();
46   data_dumper_.DumpRaw("sample_rate_hz", sample_rate_hz);
47   calls_since_last_limiter_log_ = 0;
48 }
49 
Process(AudioBuffer * audio)50 void GainController2::Process(AudioBuffer* audio) {
51   data_dumper_.DumpRaw("agc2_notified_analog_level", analog_level_);
52   AudioFrameView<float> float_frame(audio->channels(), audio->num_channels(),
53                                     audio->num_frames());
54   // Apply fixed gain first, then the adaptive one.
55   gain_applier_.ApplyGain(float_frame);
56   if (adaptive_agc_) {
57     adaptive_agc_->Process(float_frame, limiter_.LastAudioLevel());
58   }
59   limiter_.Process(float_frame);
60 
61   // Log limiter stats every 30 seconds.
62   ++calls_since_last_limiter_log_;
63   if (calls_since_last_limiter_log_ == 3000) {
64     calls_since_last_limiter_log_ = 0;
65     InterpolatedGainCurve::Stats stats = limiter_.GetGainCurveStats();
66     RTC_LOG(LS_INFO) << "AGC2 limiter stats"
67                      << " | identity: " << stats.look_ups_identity_region
68                      << " | knee: " << stats.look_ups_knee_region
69                      << " | limiter: " << stats.look_ups_limiter_region
70                      << " | saturation: " << stats.look_ups_saturation_region;
71   }
72 }
73 
NotifyAnalogLevel(int level)74 void GainController2::NotifyAnalogLevel(int level) {
75   if (analog_level_ != level && adaptive_agc_) {
76     adaptive_agc_->HandleInputGainChange();
77   }
78   analog_level_ = level;
79 }
80 
ApplyConfig(const AudioProcessing::Config::GainController2 & config)81 void GainController2::ApplyConfig(
82     const AudioProcessing::Config::GainController2& config) {
83   RTC_DCHECK(Validate(config));
84 
85   config_ = config;
86   if (config.fixed_digital.gain_db != config_.fixed_digital.gain_db) {
87     // Reset the limiter to quickly react on abrupt level changes caused by
88     // large changes of the fixed gain.
89     limiter_.Reset();
90   }
91   gain_applier_.SetGainFactor(DbToRatio(config_.fixed_digital.gain_db));
92   if (config_.adaptive_digital.enabled) {
93     adaptive_agc_ =
94         std::make_unique<AdaptiveAgc>(&data_dumper_, config_.adaptive_digital);
95   } else {
96     adaptive_agc_.reset();
97   }
98 }
99 
Validate(const AudioProcessing::Config::GainController2 & config)100 bool GainController2::Validate(
101     const AudioProcessing::Config::GainController2& config) {
102   const auto& fixed = config.fixed_digital;
103   const auto& adaptive = config.adaptive_digital;
104   return fixed.gain_db >= 0.f && fixed.gain_db < 50.f &&
105          adaptive.vad_probability_attack > 0.f &&
106          adaptive.vad_probability_attack <= 1.f &&
107          adaptive.level_estimator_adjacent_speech_frames_threshold >= 1 &&
108          adaptive.initial_saturation_margin_db >= 0.f &&
109          adaptive.initial_saturation_margin_db <= 100.f &&
110          adaptive.extra_saturation_margin_db >= 0.f &&
111          adaptive.extra_saturation_margin_db <= 100.f &&
112          adaptive.gain_applier_adjacent_speech_frames_threshold >= 1 &&
113          adaptive.max_gain_change_db_per_second > 0.f &&
114          adaptive.max_output_noise_level_dbfs <= 0.f;
115 }
116 
117 }  // namespace webrtc
118