blob: 4d84b2416ab6c3b6f8767f2cd4e58b3f315fd1cf [file] [log] [blame]
niklase@google.com470e71d2011-07-07 08:21:25 +00001/*
bjornv@webrtc.org0c6f9312012-01-30 09:39:08 +00002 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
niklase@google.com470e71d2011-07-07 08:21:25 +00003 *
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
pbos@webrtc.org7fad4b82013-05-28 08:11:59 +000011#include "webrtc/modules/audio_processing/gain_control_impl.h"
niklase@google.com470e71d2011-07-07 08:21:25 +000012
pbos@webrtc.org12dc1a32013-08-05 16:22:53 +000013#include <assert.h>
niklase@google.com470e71d2011-07-07 08:21:25 +000014
andrew@webrtc.org56e4a052014-02-27 22:23:17 +000015#include "webrtc/modules/audio_processing/audio_buffer.h"
bjornv@webrtc.orgb395a5e2014-12-16 10:38:10 +000016#include "webrtc/modules/audio_processing/agc/legacy/gain_control.h"
Henrik Kjellander98f53512015-10-28 18:17:40 +010017#include "webrtc/system_wrappers/include/critical_section_wrapper.h"
niklase@google.com470e71d2011-07-07 08:21:25 +000018
niklase@google.com470e71d2011-07-07 08:21:25 +000019namespace webrtc {
20
21typedef void Handle;
22
niklase@google.com470e71d2011-07-07 08:21:25 +000023namespace {
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +000024int16_t MapSetting(GainControl::Mode mode) {
niklase@google.com470e71d2011-07-07 08:21:25 +000025 switch (mode) {
26 case GainControl::kAdaptiveAnalog:
27 return kAgcModeAdaptiveAnalog;
niklase@google.com470e71d2011-07-07 08:21:25 +000028 case GainControl::kAdaptiveDigital:
29 return kAgcModeAdaptiveDigital;
niklase@google.com470e71d2011-07-07 08:21:25 +000030 case GainControl::kFixedDigital:
31 return kAgcModeFixedDigital;
niklase@google.com470e71d2011-07-07 08:21:25 +000032 }
andrew@webrtc.org648af742012-02-08 01:57:29 +000033 assert(false);
mflodman@webrtc.org657b2a42012-02-06 11:06:01 +000034 return -1;
niklase@google.com470e71d2011-07-07 08:21:25 +000035}
36} // namespace
37
peah4d291f72015-11-16 23:52:25 -080038const size_t GainControlImpl::kAllowedValuesOfSamplesPerFrame1;
39const size_t GainControlImpl::kAllowedValuesOfSamplesPerFrame2;
40
andrew@webrtc.org56e4a052014-02-27 22:23:17 +000041GainControlImpl::GainControlImpl(const AudioProcessing* apm,
42 CriticalSectionWrapper* crit)
peah4d291f72015-11-16 23:52:25 -080043 : ProcessingComponent(),
44 apm_(apm),
45 crit_(crit),
46 mode_(kAdaptiveAnalog),
47 minimum_capture_level_(0),
48 maximum_capture_level_(255),
49 limiter_enabled_(true),
50 target_level_dbfs_(3),
51 compression_gain_db_(9),
52 analog_capture_level_(0),
53 was_analog_level_set_(false),
54 stream_is_saturated_(false),
55 render_queue_element_max_size_(0) {
56 AllocateRenderQueue();
57}
niklase@google.com470e71d2011-07-07 08:21:25 +000058
59GainControlImpl::~GainControlImpl() {}
60
61int GainControlImpl::ProcessRenderAudio(AudioBuffer* audio) {
62 if (!is_component_enabled()) {
63 return apm_->kNoError;
64 }
65
aluebs@webrtc.orgd35a5c32015-02-10 22:52:15 +000066 assert(audio->num_frames_per_band() <= 160);
niklase@google.com470e71d2011-07-07 08:21:25 +000067
peah4d291f72015-11-16 23:52:25 -080068 render_queue_buffer_.resize(0);
niklase@google.com470e71d2011-07-07 08:21:25 +000069 for (int i = 0; i < num_handles(); i++) {
70 Handle* my_handle = static_cast<Handle*>(handle(i));
peah4d291f72015-11-16 23:52:25 -080071 int err =
72 WebRtcAgc_GetAddFarendError(my_handle, audio->num_frames_per_band());
niklase@google.com470e71d2011-07-07 08:21:25 +000073
peah4d291f72015-11-16 23:52:25 -080074 if (err != apm_->kNoError)
niklase@google.com470e71d2011-07-07 08:21:25 +000075 return GetHandleError(my_handle);
peah4d291f72015-11-16 23:52:25 -080076
77 // Buffer the samples in the render queue.
78 render_queue_buffer_.insert(
79 render_queue_buffer_.end(), audio->mixed_low_pass_data(),
80 (audio->mixed_low_pass_data() + audio->num_frames_per_band()));
81 }
82
83 // Insert the samples into the queue.
84 if (!render_signal_queue_->Insert(&render_queue_buffer_)) {
85 ReadQueuedRenderData();
86
87 // Retry the insert (should always work).
88 RTC_DCHECK_EQ(render_signal_queue_->Insert(&render_queue_buffer_), true);
niklase@google.com470e71d2011-07-07 08:21:25 +000089 }
90
91 return apm_->kNoError;
92}
93
peah4d291f72015-11-16 23:52:25 -080094// Read chunks of data that were received and queued on the render side from
95// a queue. All the data chunks are buffered into the farend signal of the AGC.
96void GainControlImpl::ReadQueuedRenderData() {
97 if (!is_component_enabled()) {
98 return;
99 }
100
101 while (render_signal_queue_->Remove(&capture_queue_buffer_)) {
102 int buffer_index = 0;
103 const int num_frames_per_band =
104 capture_queue_buffer_.size() / num_handles();
105 for (int i = 0; i < num_handles(); i++) {
106 Handle* my_handle = static_cast<Handle*>(handle(i));
107 WebRtcAgc_AddFarend(my_handle, &capture_queue_buffer_[buffer_index],
108 num_frames_per_band);
109
110 buffer_index += num_frames_per_band;
111 }
112 }
113}
114
niklase@google.com470e71d2011-07-07 08:21:25 +0000115int GainControlImpl::AnalyzeCaptureAudio(AudioBuffer* audio) {
116 if (!is_component_enabled()) {
117 return apm_->kNoError;
118 }
119
aluebs@webrtc.orgd35a5c32015-02-10 22:52:15 +0000120 assert(audio->num_frames_per_band() <= 160);
niklase@google.com470e71d2011-07-07 08:21:25 +0000121 assert(audio->num_channels() == num_handles());
122
123 int err = apm_->kNoError;
124
125 if (mode_ == kAdaptiveAnalog) {
andrew@webrtc.org60730cf2014-01-07 17:45:09 +0000126 capture_levels_.assign(num_handles(), analog_capture_level_);
niklase@google.com470e71d2011-07-07 08:21:25 +0000127 for (int i = 0; i < num_handles(); i++) {
128 Handle* my_handle = static_cast<Handle*>(handle(i));
129 err = WebRtcAgc_AddMic(
130 my_handle,
aluebs@webrtc.orgcf6d0b62014-12-16 20:56:09 +0000131 audio->split_bands(i),
132 audio->num_bands(),
Peter Kastingdce40cf2015-08-24 14:52:23 -0700133 audio->num_frames_per_band());
niklase@google.com470e71d2011-07-07 08:21:25 +0000134
135 if (err != apm_->kNoError) {
136 return GetHandleError(my_handle);
137 }
138 }
139 } else if (mode_ == kAdaptiveDigital) {
140
141 for (int i = 0; i < num_handles(); i++) {
142 Handle* my_handle = static_cast<Handle*>(handle(i));
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000143 int32_t capture_level_out = 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000144
145 err = WebRtcAgc_VirtualMic(
146 my_handle,
aluebs@webrtc.orgcf6d0b62014-12-16 20:56:09 +0000147 audio->split_bands(i),
148 audio->num_bands(),
Peter Kastingdce40cf2015-08-24 14:52:23 -0700149 audio->num_frames_per_band(),
niklase@google.com470e71d2011-07-07 08:21:25 +0000150 analog_capture_level_,
151 &capture_level_out);
152
153 capture_levels_[i] = capture_level_out;
154
155 if (err != apm_->kNoError) {
156 return GetHandleError(my_handle);
157 }
158
159 }
160 }
161
162 return apm_->kNoError;
163}
164
165int GainControlImpl::ProcessCaptureAudio(AudioBuffer* audio) {
166 if (!is_component_enabled()) {
167 return apm_->kNoError;
168 }
169
170 if (mode_ == kAdaptiveAnalog && !was_analog_level_set_) {
171 return apm_->kStreamParameterNotSetError;
172 }
173
aluebs@webrtc.orgd35a5c32015-02-10 22:52:15 +0000174 assert(audio->num_frames_per_band() <= 160);
niklase@google.com470e71d2011-07-07 08:21:25 +0000175 assert(audio->num_channels() == num_handles());
176
177 stream_is_saturated_ = false;
178 for (int i = 0; i < num_handles(); i++) {
179 Handle* my_handle = static_cast<Handle*>(handle(i));
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000180 int32_t capture_level_out = 0;
181 uint8_t saturation_warning = 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000182
183 int err = WebRtcAgc_Process(
184 my_handle,
aluebs@webrtc.orgcf6d0b62014-12-16 20:56:09 +0000185 audio->split_bands_const(i),
186 audio->num_bands(),
Peter Kastingdce40cf2015-08-24 14:52:23 -0700187 audio->num_frames_per_band(),
aluebs@webrtc.orgcf6d0b62014-12-16 20:56:09 +0000188 audio->split_bands(i),
niklase@google.com470e71d2011-07-07 08:21:25 +0000189 capture_levels_[i],
190 &capture_level_out,
191 apm_->echo_cancellation()->stream_has_echo(),
192 &saturation_warning);
193
194 if (err != apm_->kNoError) {
195 return GetHandleError(my_handle);
196 }
197
198 capture_levels_[i] = capture_level_out;
199 if (saturation_warning == 1) {
200 stream_is_saturated_ = true;
201 }
202 }
203
204 if (mode_ == kAdaptiveAnalog) {
205 // Take the analog level to be the average across the handles.
206 analog_capture_level_ = 0;
207 for (int i = 0; i < num_handles(); i++) {
208 analog_capture_level_ += capture_levels_[i];
209 }
210
211 analog_capture_level_ /= num_handles();
212 }
213
214 was_analog_level_set_ = false;
215 return apm_->kNoError;
216}
217
218// TODO(ajm): ensure this is called under kAdaptiveAnalog.
219int GainControlImpl::set_stream_analog_level(int level) {
peah4d291f72015-11-16 23:52:25 -0800220 // TODO(peah): Verify that this is really needed to do the reading
221 // here as well as in ProcessStream. It works since these functions
222 // are called from the same thread, but it is not nice to do it in two
223 // places if not needed.
224 ReadQueuedRenderData();
225
Brave Yao1a07a1e2015-05-21 12:42:40 +0800226 CriticalSectionScoped crit_scoped(crit_);
niklase@google.com470e71d2011-07-07 08:21:25 +0000227 was_analog_level_set_ = true;
228 if (level < minimum_capture_level_ || level > maximum_capture_level_) {
229 return apm_->kBadParameterError;
230 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000231 analog_capture_level_ = level;
232
233 return apm_->kNoError;
234}
235
236int GainControlImpl::stream_analog_level() {
237 // TODO(ajm): enable this assertion?
238 //assert(mode_ == kAdaptiveAnalog);
239
240 return analog_capture_level_;
241}
242
243int GainControlImpl::Enable(bool enable) {
andrew@webrtc.org56e4a052014-02-27 22:23:17 +0000244 CriticalSectionScoped crit_scoped(crit_);
niklase@google.com470e71d2011-07-07 08:21:25 +0000245 return EnableComponent(enable);
246}
247
248bool GainControlImpl::is_enabled() const {
249 return is_component_enabled();
250}
251
252int GainControlImpl::set_mode(Mode mode) {
andrew@webrtc.org56e4a052014-02-27 22:23:17 +0000253 CriticalSectionScoped crit_scoped(crit_);
niklase@google.com470e71d2011-07-07 08:21:25 +0000254 if (MapSetting(mode) == -1) {
255 return apm_->kBadParameterError;
256 }
257
258 mode_ = mode;
259 return Initialize();
260}
261
262GainControl::Mode GainControlImpl::mode() const {
263 return mode_;
264}
265
266int GainControlImpl::set_analog_level_limits(int minimum,
267 int maximum) {
andrew@webrtc.org56e4a052014-02-27 22:23:17 +0000268 CriticalSectionScoped crit_scoped(crit_);
niklase@google.com470e71d2011-07-07 08:21:25 +0000269 if (minimum < 0) {
270 return apm_->kBadParameterError;
271 }
272
273 if (maximum > 65535) {
274 return apm_->kBadParameterError;
275 }
276
277 if (maximum < minimum) {
278 return apm_->kBadParameterError;
279 }
280
281 minimum_capture_level_ = minimum;
282 maximum_capture_level_ = maximum;
283
284 return Initialize();
285}
286
287int GainControlImpl::analog_level_minimum() const {
288 return minimum_capture_level_;
289}
290
291int GainControlImpl::analog_level_maximum() const {
292 return maximum_capture_level_;
293}
294
295bool GainControlImpl::stream_is_saturated() const {
296 return stream_is_saturated_;
297}
298
299int GainControlImpl::set_target_level_dbfs(int level) {
andrew@webrtc.org56e4a052014-02-27 22:23:17 +0000300 CriticalSectionScoped crit_scoped(crit_);
niklase@google.com470e71d2011-07-07 08:21:25 +0000301 if (level > 31 || level < 0) {
302 return apm_->kBadParameterError;
303 }
304
305 target_level_dbfs_ = level;
306 return Configure();
307}
308
309int GainControlImpl::target_level_dbfs() const {
310 return target_level_dbfs_;
311}
312
313int GainControlImpl::set_compression_gain_db(int gain) {
andrew@webrtc.org56e4a052014-02-27 22:23:17 +0000314 CriticalSectionScoped crit_scoped(crit_);
niklase@google.com470e71d2011-07-07 08:21:25 +0000315 if (gain < 0 || gain > 90) {
316 return apm_->kBadParameterError;
317 }
318
319 compression_gain_db_ = gain;
320 return Configure();
321}
322
323int GainControlImpl::compression_gain_db() const {
324 return compression_gain_db_;
325}
326
327int GainControlImpl::enable_limiter(bool enable) {
andrew@webrtc.org56e4a052014-02-27 22:23:17 +0000328 CriticalSectionScoped crit_scoped(crit_);
niklase@google.com470e71d2011-07-07 08:21:25 +0000329 limiter_enabled_ = enable;
330 return Configure();
331}
332
333bool GainControlImpl::is_limiter_enabled() const {
334 return limiter_enabled_;
335}
336
337int GainControlImpl::Initialize() {
338 int err = ProcessingComponent::Initialize();
339 if (err != apm_->kNoError || !is_component_enabled()) {
340 return err;
341 }
342
peah4d291f72015-11-16 23:52:25 -0800343 AllocateRenderQueue();
344
kwibergcd19faf2015-11-05 05:11:19 -0800345 const int n = num_handles();
346 RTC_CHECK_GE(n, 0) << "Bad number of handles: " << n;
347 capture_levels_.assign(n, analog_capture_level_);
niklase@google.com470e71d2011-07-07 08:21:25 +0000348 return apm_->kNoError;
349}
350
peah4d291f72015-11-16 23:52:25 -0800351void GainControlImpl::AllocateRenderQueue() {
352 const size_t max_frame_size = std::max<size_t>(
353 kAllowedValuesOfSamplesPerFrame1, kAllowedValuesOfSamplesPerFrame2);
354
355 const size_t new_render_queue_element_max_size = std::max<size_t>(
356 static_cast<size_t>(1), (max_frame_size * num_handles()));
357
358 if (new_render_queue_element_max_size > render_queue_element_max_size_) {
359 std::vector<int16_t> template_queue_element(render_queue_element_max_size_);
360
361 render_signal_queue_.reset(
362 new SwapQueue<std::vector<int16_t>, RenderQueueItemVerifier<int16_t>>(
363 kMaxNumFramesToBuffer, template_queue_element,
364 RenderQueueItemVerifier<int16_t>(render_queue_element_max_size_)));
365 } else {
366 render_signal_queue_->Clear();
367 }
368
369 render_queue_buffer_.resize(new_render_queue_element_max_size);
370 capture_queue_buffer_.resize(new_render_queue_element_max_size);
371}
372
niklase@google.com470e71d2011-07-07 08:21:25 +0000373void* GainControlImpl::CreateHandle() const {
Bjorn Volcker9345e862015-06-10 21:43:36 +0200374 return WebRtcAgc_Create();
niklase@google.com470e71d2011-07-07 08:21:25 +0000375}
376
bjornv@webrtc.org5964fe02014-04-22 06:52:28 +0000377void GainControlImpl::DestroyHandle(void* handle) const {
378 WebRtcAgc_Free(static_cast<Handle*>(handle));
niklase@google.com470e71d2011-07-07 08:21:25 +0000379}
380
381int GainControlImpl::InitializeHandle(void* handle) const {
382 return WebRtcAgc_Init(static_cast<Handle*>(handle),
383 minimum_capture_level_,
384 maximum_capture_level_,
385 MapSetting(mode_),
andrew@webrtc.orgddbb8a22014-04-22 21:00:04 +0000386 apm_->proc_sample_rate_hz());
niklase@google.com470e71d2011-07-07 08:21:25 +0000387}
388
389int GainControlImpl::ConfigureHandle(void* handle) const {
pbos@webrtc.orge468bc92014-12-18 09:11:33 +0000390 WebRtcAgcConfig config;
niklase@google.com470e71d2011-07-07 08:21:25 +0000391 // TODO(ajm): Flip the sign here (since AGC expects a positive value) if we
392 // change the interface.
393 //assert(target_level_dbfs_ <= 0);
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000394 //config.targetLevelDbfs = static_cast<int16_t>(-target_level_dbfs_);
395 config.targetLevelDbfs = static_cast<int16_t>(target_level_dbfs_);
niklase@google.com470e71d2011-07-07 08:21:25 +0000396 config.compressionGaindB =
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000397 static_cast<int16_t>(compression_gain_db_);
niklase@google.com470e71d2011-07-07 08:21:25 +0000398 config.limiterEnable = limiter_enabled_;
399
400 return WebRtcAgc_set_config(static_cast<Handle*>(handle), config);
401}
402
403int GainControlImpl::num_handles_required() const {
404 return apm_->num_output_channels();
405}
406
407int GainControlImpl::GetHandleError(void* handle) const {
408 // The AGC has no get_error() function.
409 // (Despite listing errors in its interface...)
410 assert(handle != NULL);
411 return apm_->kUnspecifiedError;
412}
413} // namespace webrtc