1 /*
2  *  Copyright (c) 2012 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_control_impl.h"
12 
13 #include <cstdint>
14 
15 #include "absl/types/optional.h"
16 #include "modules/audio_processing/agc/legacy/gain_control.h"
17 #include "modules/audio_processing/audio_buffer.h"
18 #include "modules/audio_processing/include/audio_processing.h"
19 #include "modules/audio_processing/logging/apm_data_dumper.h"
20 #include "rtc_base/checks.h"
21 #include "rtc_base/constructormagic.h"
22 
23 namespace webrtc {
24 
25 typedef void Handle;
26 
27 namespace {
MapSetting(GainControl::Mode mode)28 int16_t MapSetting(GainControl::Mode mode) {
29   switch (mode) {
30     case GainControl::kAdaptiveAnalog:
31       return kAgcModeAdaptiveAnalog;
32     case GainControl::kAdaptiveDigital:
33       return kAgcModeAdaptiveDigital;
34     case GainControl::kFixedDigital:
35       return kAgcModeFixedDigital;
36   }
37   RTC_NOTREACHED();
38   return -1;
39 }
40 
41 }  // namespace
42 
43 class GainControlImpl::GainController {
44  public:
GainController()45   explicit GainController() {
46     state_ = WebRtcAgc_Create();
47     RTC_CHECK(state_);
48   }
49 
~GainController()50   ~GainController() {
51     RTC_DCHECK(state_);
52     WebRtcAgc_Free(state_);
53   }
54 
state()55   Handle* state() {
56     RTC_DCHECK(state_);
57     return state_;
58   }
59 
Initialize(int minimum_capture_level,int maximum_capture_level,Mode mode,int sample_rate_hz,int capture_level)60   void Initialize(int minimum_capture_level,
61                   int maximum_capture_level,
62                   Mode mode,
63                   int sample_rate_hz,
64                   int capture_level) {
65     RTC_DCHECK(state_);
66     int error =
67         WebRtcAgc_Init(state_, minimum_capture_level, maximum_capture_level,
68                        MapSetting(mode), sample_rate_hz);
69     RTC_DCHECK_EQ(0, error);
70 
71     set_capture_level(capture_level);
72   }
73 
set_capture_level(int capture_level)74   void set_capture_level(int capture_level) { capture_level_ = capture_level; }
75 
get_capture_level()76   int get_capture_level() {
77     RTC_DCHECK(capture_level_);
78     return *capture_level_;
79   }
80 
81  private:
82   Handle* state_;
83   // TODO(peah): Remove the optional once the initialization is moved into the
84   // ctor.
85   absl::optional<int> capture_level_;
86 
87   RTC_DISALLOW_COPY_AND_ASSIGN(GainController);
88 };
89 
90 int GainControlImpl::instance_counter_ = 0;
91 
GainControlImpl(rtc::CriticalSection * crit_render,rtc::CriticalSection * crit_capture)92 GainControlImpl::GainControlImpl(rtc::CriticalSection* crit_render,
93                                  rtc::CriticalSection* crit_capture)
94     : crit_render_(crit_render),
95       crit_capture_(crit_capture),
96       data_dumper_(new ApmDataDumper(instance_counter_)),
97       mode_(kAdaptiveAnalog),
98       minimum_capture_level_(0),
99       maximum_capture_level_(255),
100       limiter_enabled_(true),
101       target_level_dbfs_(3),
102       compression_gain_db_(9),
103       analog_capture_level_(0),
104       was_analog_level_set_(false),
105       stream_is_saturated_(false) {
106   RTC_DCHECK(crit_render);
107   RTC_DCHECK(crit_capture);
108 }
109 
~GainControlImpl()110 GainControlImpl::~GainControlImpl() {}
111 
ProcessRenderAudio(rtc::ArrayView<const int16_t> packed_render_audio)112 void GainControlImpl::ProcessRenderAudio(
113     rtc::ArrayView<const int16_t> packed_render_audio) {
114   rtc::CritScope cs_capture(crit_capture_);
115   if (!enabled_) {
116     return;
117   }
118 
119   for (auto& gain_controller : gain_controllers_) {
120     WebRtcAgc_AddFarend(gain_controller->state(), packed_render_audio.data(),
121                         packed_render_audio.size());
122   }
123 }
124 
PackRenderAudioBuffer(AudioBuffer * audio,std::vector<int16_t> * packed_buffer)125 void GainControlImpl::PackRenderAudioBuffer(
126     AudioBuffer* audio,
127     std::vector<int16_t>* packed_buffer) {
128   RTC_DCHECK_GE(160, audio->num_frames_per_band());
129 
130   packed_buffer->clear();
131   packed_buffer->insert(
132       packed_buffer->end(), audio->mixed_low_pass_data(),
133       (audio->mixed_low_pass_data() + audio->num_frames_per_band()));
134 }
135 
AnalyzeCaptureAudio(AudioBuffer * audio)136 int GainControlImpl::AnalyzeCaptureAudio(AudioBuffer* audio) {
137   rtc::CritScope cs(crit_capture_);
138 
139   if (!enabled_) {
140     return AudioProcessing::kNoError;
141   }
142 
143   RTC_DCHECK(num_proc_channels_);
144   RTC_DCHECK_GE(160, audio->num_frames_per_band());
145   RTC_DCHECK_EQ(audio->num_channels(), *num_proc_channels_);
146   RTC_DCHECK_LE(*num_proc_channels_, gain_controllers_.size());
147 
148   if (mode_ == kAdaptiveAnalog) {
149     int capture_channel = 0;
150     for (auto& gain_controller : gain_controllers_) {
151       gain_controller->set_capture_level(analog_capture_level_);
152       int err = WebRtcAgc_AddMic(
153           gain_controller->state(), audio->split_bands(capture_channel),
154           audio->num_bands(), audio->num_frames_per_band());
155 
156       if (err != AudioProcessing::kNoError) {
157         return AudioProcessing::kUnspecifiedError;
158       }
159       ++capture_channel;
160     }
161   } else if (mode_ == kAdaptiveDigital) {
162     int capture_channel = 0;
163     for (auto& gain_controller : gain_controllers_) {
164       int32_t capture_level_out = 0;
165       int err = WebRtcAgc_VirtualMic(
166           gain_controller->state(), audio->split_bands(capture_channel),
167           audio->num_bands(), audio->num_frames_per_band(),
168           analog_capture_level_, &capture_level_out);
169 
170       gain_controller->set_capture_level(capture_level_out);
171 
172       if (err != AudioProcessing::kNoError) {
173         return AudioProcessing::kUnspecifiedError;
174       }
175       ++capture_channel;
176     }
177   }
178 
179   return AudioProcessing::kNoError;
180 }
181 
ProcessCaptureAudio(AudioBuffer * audio,bool stream_has_echo)182 int GainControlImpl::ProcessCaptureAudio(AudioBuffer* audio,
183                                          bool stream_has_echo) {
184   rtc::CritScope cs(crit_capture_);
185 
186   if (!enabled_) {
187     return AudioProcessing::kNoError;
188   }
189 
190   if (mode_ == kAdaptiveAnalog && !was_analog_level_set_) {
191     return AudioProcessing::kStreamParameterNotSetError;
192   }
193 
194   RTC_DCHECK(num_proc_channels_);
195   RTC_DCHECK_GE(160, audio->num_frames_per_band());
196   RTC_DCHECK_EQ(audio->num_channels(), *num_proc_channels_);
197 
198   stream_is_saturated_ = false;
199   int capture_channel = 0;
200   for (auto& gain_controller : gain_controllers_) {
201     int32_t capture_level_out = 0;
202     uint8_t saturation_warning = 0;
203 
204     // The call to stream_has_echo() is ok from a deadlock perspective
205     // as the capture lock is allready held.
206     int err = WebRtcAgc_Process(
207         gain_controller->state(), audio->split_bands_const(capture_channel),
208         audio->num_bands(), audio->num_frames_per_band(),
209         audio->split_bands(capture_channel),
210         gain_controller->get_capture_level(), &capture_level_out,
211         stream_has_echo, &saturation_warning);
212 
213     if (err != AudioProcessing::kNoError) {
214       return AudioProcessing::kUnspecifiedError;
215     }
216 
217     gain_controller->set_capture_level(capture_level_out);
218     if (saturation_warning == 1) {
219       stream_is_saturated_ = true;
220     }
221 
222     ++capture_channel;
223   }
224 
225   RTC_DCHECK_LT(0ul, *num_proc_channels_);
226   if (mode_ == kAdaptiveAnalog) {
227     // Take the analog level to be the average across the handles.
228     analog_capture_level_ = 0;
229     for (auto& gain_controller : gain_controllers_) {
230       analog_capture_level_ += gain_controller->get_capture_level();
231     }
232 
233     analog_capture_level_ /= (*num_proc_channels_);
234   }
235 
236   was_analog_level_set_ = false;
237   return AudioProcessing::kNoError;
238 }
239 
compression_gain_db() const240 int GainControlImpl::compression_gain_db() const {
241   rtc::CritScope cs(crit_capture_);
242   return compression_gain_db_;
243 }
244 
245 // TODO(ajm): ensure this is called under kAdaptiveAnalog.
set_stream_analog_level(int level)246 int GainControlImpl::set_stream_analog_level(int level) {
247   rtc::CritScope cs(crit_capture_);
248   data_dumper_->DumpRaw("gain_control_set_stream_analog_level", 1, &level);
249 
250   was_analog_level_set_ = true;
251   if (level < minimum_capture_level_ || level > maximum_capture_level_) {
252     return AudioProcessing::kBadParameterError;
253   }
254   analog_capture_level_ = level;
255 
256   return AudioProcessing::kNoError;
257 }
258 
stream_analog_level()259 int GainControlImpl::stream_analog_level() {
260   rtc::CritScope cs(crit_capture_);
261   data_dumper_->DumpRaw("gain_control_stream_analog_level", 1,
262                         &analog_capture_level_);
263   // TODO(ajm): enable this assertion?
264   // RTC_DCHECK_EQ(kAdaptiveAnalog, mode_);
265 
266   return analog_capture_level_;
267 }
268 
Enable(bool enable)269 int GainControlImpl::Enable(bool enable) {
270   rtc::CritScope cs_render(crit_render_);
271   rtc::CritScope cs_capture(crit_capture_);
272   if (enable && !enabled_) {
273     enabled_ = enable;  // Must be set before Initialize() is called.
274 
275     RTC_DCHECK(num_proc_channels_);
276     RTC_DCHECK(sample_rate_hz_);
277     Initialize(*num_proc_channels_, *sample_rate_hz_);
278   } else {
279     enabled_ = enable;
280   }
281   return AudioProcessing::kNoError;
282 }
283 
is_enabled() const284 bool GainControlImpl::is_enabled() const {
285   rtc::CritScope cs(crit_capture_);
286   return enabled_;
287 }
288 
set_mode(Mode mode)289 int GainControlImpl::set_mode(Mode mode) {
290   rtc::CritScope cs_render(crit_render_);
291   rtc::CritScope cs_capture(crit_capture_);
292   if (MapSetting(mode) == -1) {
293     return AudioProcessing::kBadParameterError;
294   }
295 
296   mode_ = mode;
297   RTC_DCHECK(num_proc_channels_);
298   RTC_DCHECK(sample_rate_hz_);
299   Initialize(*num_proc_channels_, *sample_rate_hz_);
300   return AudioProcessing::kNoError;
301 }
302 
mode() const303 GainControl::Mode GainControlImpl::mode() const {
304   rtc::CritScope cs(crit_capture_);
305   return mode_;
306 }
307 
set_analog_level_limits(int minimum,int maximum)308 int GainControlImpl::set_analog_level_limits(int minimum, int maximum) {
309   if (minimum < 0) {
310     return AudioProcessing::kBadParameterError;
311   }
312 
313   if (maximum > 65535) {
314     return AudioProcessing::kBadParameterError;
315   }
316 
317   if (maximum < minimum) {
318     return AudioProcessing::kBadParameterError;
319   }
320 
321   size_t num_proc_channels_local = 0u;
322   int sample_rate_hz_local = 0;
323   {
324     rtc::CritScope cs(crit_capture_);
325 
326     minimum_capture_level_ = minimum;
327     maximum_capture_level_ = maximum;
328 
329     RTC_DCHECK(num_proc_channels_);
330     RTC_DCHECK(sample_rate_hz_);
331     num_proc_channels_local = *num_proc_channels_;
332     sample_rate_hz_local = *sample_rate_hz_;
333   }
334   Initialize(num_proc_channels_local, sample_rate_hz_local);
335   return AudioProcessing::kNoError;
336 }
337 
analog_level_minimum() const338 int GainControlImpl::analog_level_minimum() const {
339   rtc::CritScope cs(crit_capture_);
340   return minimum_capture_level_;
341 }
342 
analog_level_maximum() const343 int GainControlImpl::analog_level_maximum() const {
344   rtc::CritScope cs(crit_capture_);
345   return maximum_capture_level_;
346 }
347 
stream_is_saturated() const348 bool GainControlImpl::stream_is_saturated() const {
349   rtc::CritScope cs(crit_capture_);
350   return stream_is_saturated_;
351 }
352 
set_target_level_dbfs(int level)353 int GainControlImpl::set_target_level_dbfs(int level) {
354   if (level > 31 || level < 0) {
355     return AudioProcessing::kBadParameterError;
356   }
357   {
358     rtc::CritScope cs(crit_capture_);
359     target_level_dbfs_ = level;
360   }
361   return Configure();
362 }
363 
target_level_dbfs() const364 int GainControlImpl::target_level_dbfs() const {
365   rtc::CritScope cs(crit_capture_);
366   return target_level_dbfs_;
367 }
368 
set_compression_gain_db(int gain)369 int GainControlImpl::set_compression_gain_db(int gain) {
370   if (gain < 0 || gain > 90) {
371     return AudioProcessing::kBadParameterError;
372   }
373   {
374     rtc::CritScope cs(crit_capture_);
375     compression_gain_db_ = gain;
376   }
377   return Configure();
378 }
379 
enable_limiter(bool enable)380 int GainControlImpl::enable_limiter(bool enable) {
381   {
382     rtc::CritScope cs(crit_capture_);
383     limiter_enabled_ = enable;
384   }
385   return Configure();
386 }
387 
is_limiter_enabled() const388 bool GainControlImpl::is_limiter_enabled() const {
389   rtc::CritScope cs(crit_capture_);
390   return limiter_enabled_;
391 }
392 
Initialize(size_t num_proc_channels,int sample_rate_hz)393 void GainControlImpl::Initialize(size_t num_proc_channels, int sample_rate_hz) {
394   rtc::CritScope cs_render(crit_render_);
395   rtc::CritScope cs_capture(crit_capture_);
396   data_dumper_->InitiateNewSetOfRecordings();
397 
398   num_proc_channels_ = num_proc_channels;
399   sample_rate_hz_ = sample_rate_hz;
400 
401   if (!enabled_) {
402     return;
403   }
404 
405   gain_controllers_.resize(*num_proc_channels_);
406   for (auto& gain_controller : gain_controllers_) {
407     if (!gain_controller) {
408       gain_controller.reset(new GainController());
409     }
410     gain_controller->Initialize(minimum_capture_level_, maximum_capture_level_,
411                                 mode_, *sample_rate_hz_, analog_capture_level_);
412   }
413 
414   Configure();
415 }
416 
Configure()417 int GainControlImpl::Configure() {
418   rtc::CritScope cs_render(crit_render_);
419   rtc::CritScope cs_capture(crit_capture_);
420   WebRtcAgcConfig config;
421   // TODO(ajm): Flip the sign here (since AGC expects a positive value) if we
422   //            change the interface.
423   // RTC_DCHECK_LE(target_level_dbfs_, 0);
424   // config.targetLevelDbfs = static_cast<int16_t>(-target_level_dbfs_);
425   config.targetLevelDbfs = static_cast<int16_t>(target_level_dbfs_);
426   config.compressionGaindB = static_cast<int16_t>(compression_gain_db_);
427   config.limiterEnable = limiter_enabled_;
428 
429   int error = AudioProcessing::kNoError;
430   for (auto& gain_controller : gain_controllers_) {
431     const int handle_error =
432         WebRtcAgc_set_config(gain_controller->state(), config);
433     if (handle_error != AudioProcessing::kNoError) {
434       error = handle_error;
435     }
436   }
437   return error;
438 }
439 }  // namespace webrtc
440