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