blob: 47ee802483b0a2be93d4c373756aece2f9b35de7 [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
bjornv@webrtc.org21a2fc92013-02-15 17:01:03 +000011#include "webrtc/modules/audio_processing/echo_cancellation_impl.h"
niklase@google.com470e71d2011-07-07 08:21:25 +000012
bjornv@webrtc.org21a2fc92013-02-15 17:01:03 +000013#include <assert.h>
niklase@google.com470e71d2011-07-07 08:21:25 +000014#include <string.h>
15
bjornv@webrtc.org21a2fc92013-02-15 17:01:03 +000016#include "webrtc/modules/audio_processing/audio_buffer.h"
17#include "webrtc/modules/audio_processing/audio_processing_impl.h"
18#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
niklase@google.com470e71d2011-07-07 08:21:25 +000019
asapersson@webrtc.orgce014d92013-09-25 12:27:27 +000020#include "webrtc/modules/audio_processing/aec/include/echo_cancellation.h"
21
niklase@google.com470e71d2011-07-07 08:21:25 +000022namespace webrtc {
23
24typedef void Handle;
25
26namespace {
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +000027int16_t MapSetting(EchoCancellation::SuppressionLevel level) {
niklase@google.com470e71d2011-07-07 08:21:25 +000028 switch (level) {
29 case EchoCancellation::kLowSuppression:
30 return kAecNlpConservative;
31 case EchoCancellation::kModerateSuppression:
32 return kAecNlpModerate;
33 case EchoCancellation::kHighSuppression:
34 return kAecNlpAggressive;
niklase@google.com470e71d2011-07-07 08:21:25 +000035 }
andrew@webrtc.org648af742012-02-08 01:57:29 +000036 assert(false);
mflodman@webrtc.org657b2a42012-02-06 11:06:01 +000037 return -1;
niklase@google.com470e71d2011-07-07 08:21:25 +000038}
39
andrew@webrtc.org648af742012-02-08 01:57:29 +000040AudioProcessing::Error MapError(int err) {
niklase@google.com470e71d2011-07-07 08:21:25 +000041 switch (err) {
42 case AEC_UNSUPPORTED_FUNCTION_ERROR:
43 return AudioProcessing::kUnsupportedFunctionError;
niklase@google.com470e71d2011-07-07 08:21:25 +000044 case AEC_BAD_PARAMETER_ERROR:
45 return AudioProcessing::kBadParameterError;
niklase@google.com470e71d2011-07-07 08:21:25 +000046 case AEC_BAD_PARAMETER_WARNING:
47 return AudioProcessing::kBadStreamParameterWarning;
niklase@google.com470e71d2011-07-07 08:21:25 +000048 default:
49 // AEC_UNSPECIFIED_ERROR
50 // AEC_UNINITIALIZED_ERROR
51 // AEC_NULL_POINTER_ERROR
52 return AudioProcessing::kUnspecifiedError;
53 }
54}
55} // namespace
56
andrew@webrtc.org61e596f2013-07-25 18:28:29 +000057EchoCancellationImplWrapper* EchoCancellationImplWrapper::Create(
58 const AudioProcessingImpl* audioproc) {
59 return new EchoCancellationImpl(audioproc);
60}
61
niklase@google.com470e71d2011-07-07 08:21:25 +000062EchoCancellationImpl::EchoCancellationImpl(const AudioProcessingImpl* apm)
63 : ProcessingComponent(apm),
64 apm_(apm),
65 drift_compensation_enabled_(false),
66 metrics_enabled_(false),
67 suppression_level_(kModerateSuppression),
68 device_sample_rate_hz_(48000),
69 stream_drift_samples_(0),
70 was_stream_drift_set_(false),
bjornv@google.com1ba3dbe2011-10-03 08:18:10 +000071 stream_has_echo_(false),
asapersson@webrtc.orgce014d92013-09-25 12:27:27 +000072 delay_logging_enabled_(false) {}
niklase@google.com470e71d2011-07-07 08:21:25 +000073
74EchoCancellationImpl::~EchoCancellationImpl() {}
75
76int EchoCancellationImpl::ProcessRenderAudio(const AudioBuffer* audio) {
77 if (!is_component_enabled()) {
78 return apm_->kNoError;
79 }
80
81 assert(audio->samples_per_split_channel() <= 160);
82 assert(audio->num_channels() == apm_->num_reverse_channels());
83
84 int err = apm_->kNoError;
85
86 // The ordering convention must be followed to pass to the correct AEC.
87 size_t handle_index = 0;
88 for (int i = 0; i < apm_->num_output_channels(); i++) {
89 for (int j = 0; j < audio->num_channels(); j++) {
90 Handle* my_handle = static_cast<Handle*>(handle(handle_index));
91 err = WebRtcAec_BufferFarend(
92 my_handle,
93 audio->low_pass_split_data(j),
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +000094 static_cast<int16_t>(audio->samples_per_split_channel()));
niklase@google.com470e71d2011-07-07 08:21:25 +000095
96 if (err != apm_->kNoError) {
97 return GetHandleError(my_handle); // TODO(ajm): warning possible?
98 }
99
100 handle_index++;
101 }
102 }
103
104 return apm_->kNoError;
105}
106
107int EchoCancellationImpl::ProcessCaptureAudio(AudioBuffer* audio) {
108 if (!is_component_enabled()) {
109 return apm_->kNoError;
110 }
111
112 if (!apm_->was_stream_delay_set()) {
113 return apm_->kStreamParameterNotSetError;
114 }
115
116 if (drift_compensation_enabled_ && !was_stream_drift_set_) {
117 return apm_->kStreamParameterNotSetError;
118 }
119
120 assert(audio->samples_per_split_channel() <= 160);
121 assert(audio->num_channels() == apm_->num_output_channels());
122
123 int err = apm_->kNoError;
124
125 // The ordering convention must be followed to pass to the correct AEC.
126 size_t handle_index = 0;
127 stream_has_echo_ = false;
128 for (int i = 0; i < audio->num_channels(); i++) {
129 for (int j = 0; j < apm_->num_reverse_channels(); j++) {
130 Handle* my_handle = handle(handle_index);
131 err = WebRtcAec_Process(
132 my_handle,
133 audio->low_pass_split_data(i),
134 audio->high_pass_split_data(i),
135 audio->low_pass_split_data(i),
136 audio->high_pass_split_data(i),
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000137 static_cast<int16_t>(audio->samples_per_split_channel()),
niklase@google.com470e71d2011-07-07 08:21:25 +0000138 apm_->stream_delay_ms(),
139 stream_drift_samples_);
140
141 if (err != apm_->kNoError) {
142 err = GetHandleError(my_handle);
143 // TODO(ajm): Figure out how to return warnings properly.
144 if (err != apm_->kBadStreamParameterWarning) {
145 return err;
146 }
147 }
148
bjornv@webrtc.org21a2fc92013-02-15 17:01:03 +0000149 int status = 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000150 err = WebRtcAec_get_echo_status(my_handle, &status);
151 if (err != apm_->kNoError) {
152 return GetHandleError(my_handle);
153 }
154
155 if (status == 1) {
156 stream_has_echo_ = true;
157 }
158
159 handle_index++;
160 }
161 }
162
163 was_stream_drift_set_ = false;
164 return apm_->kNoError;
165}
166
167int EchoCancellationImpl::Enable(bool enable) {
andrew@webrtc.org40654032012-01-30 20:51:15 +0000168 CriticalSectionScoped crit_scoped(apm_->crit());
niklase@google.com470e71d2011-07-07 08:21:25 +0000169 // Ensure AEC and AECM are not both enabled.
170 if (enable && apm_->echo_control_mobile()->is_enabled()) {
171 return apm_->kBadParameterError;
172 }
173
174 return EnableComponent(enable);
175}
176
177bool EchoCancellationImpl::is_enabled() const {
178 return is_component_enabled();
179}
180
181int EchoCancellationImpl::set_suppression_level(SuppressionLevel level) {
andrew@webrtc.org40654032012-01-30 20:51:15 +0000182 CriticalSectionScoped crit_scoped(apm_->crit());
niklase@google.com470e71d2011-07-07 08:21:25 +0000183 if (MapSetting(level) == -1) {
184 return apm_->kBadParameterError;
185 }
186
187 suppression_level_ = level;
188 return Configure();
189}
190
191EchoCancellation::SuppressionLevel EchoCancellationImpl::suppression_level()
192 const {
193 return suppression_level_;
194}
195
196int EchoCancellationImpl::enable_drift_compensation(bool enable) {
andrew@webrtc.org40654032012-01-30 20:51:15 +0000197 CriticalSectionScoped crit_scoped(apm_->crit());
niklase@google.com470e71d2011-07-07 08:21:25 +0000198 drift_compensation_enabled_ = enable;
199 return Configure();
200}
201
202bool EchoCancellationImpl::is_drift_compensation_enabled() const {
203 return drift_compensation_enabled_;
204}
205
206int EchoCancellationImpl::set_device_sample_rate_hz(int rate) {
andrew@webrtc.org40654032012-01-30 20:51:15 +0000207 CriticalSectionScoped crit_scoped(apm_->crit());
niklase@google.com470e71d2011-07-07 08:21:25 +0000208 if (rate < 8000 || rate > 96000) {
209 return apm_->kBadParameterError;
210 }
211
212 device_sample_rate_hz_ = rate;
213 return Initialize();
214}
215
216int EchoCancellationImpl::device_sample_rate_hz() const {
217 return device_sample_rate_hz_;
218}
219
andrew@webrtc.org6be1e932013-03-01 18:47:28 +0000220void EchoCancellationImpl::set_stream_drift_samples(int drift) {
niklase@google.com470e71d2011-07-07 08:21:25 +0000221 was_stream_drift_set_ = true;
222 stream_drift_samples_ = drift;
niklase@google.com470e71d2011-07-07 08:21:25 +0000223}
224
225int EchoCancellationImpl::stream_drift_samples() const {
226 return stream_drift_samples_;
227}
228
229int EchoCancellationImpl::enable_metrics(bool enable) {
andrew@webrtc.org40654032012-01-30 20:51:15 +0000230 CriticalSectionScoped crit_scoped(apm_->crit());
niklase@google.com470e71d2011-07-07 08:21:25 +0000231 metrics_enabled_ = enable;
232 return Configure();
233}
234
235bool EchoCancellationImpl::are_metrics_enabled() const {
236 return metrics_enabled_;
237}
238
239// TODO(ajm): we currently just use the metrics from the first AEC. Think more
240// aboue the best way to extend this to multi-channel.
241int EchoCancellationImpl::GetMetrics(Metrics* metrics) {
andrew@webrtc.org40654032012-01-30 20:51:15 +0000242 CriticalSectionScoped crit_scoped(apm_->crit());
niklase@google.com470e71d2011-07-07 08:21:25 +0000243 if (metrics == NULL) {
244 return apm_->kNullPointerError;
245 }
246
247 if (!is_component_enabled() || !metrics_enabled_) {
248 return apm_->kNotEnabledError;
249 }
250
251 AecMetrics my_metrics;
252 memset(&my_metrics, 0, sizeof(my_metrics));
253 memset(metrics, 0, sizeof(Metrics));
254
255 Handle* my_handle = static_cast<Handle*>(handle(0));
256 int err = WebRtcAec_GetMetrics(my_handle, &my_metrics);
257 if (err != apm_->kNoError) {
258 return GetHandleError(my_handle);
259 }
260
261 metrics->residual_echo_return_loss.instant = my_metrics.rerl.instant;
262 metrics->residual_echo_return_loss.average = my_metrics.rerl.average;
263 metrics->residual_echo_return_loss.maximum = my_metrics.rerl.max;
264 metrics->residual_echo_return_loss.minimum = my_metrics.rerl.min;
265
266 metrics->echo_return_loss.instant = my_metrics.erl.instant;
267 metrics->echo_return_loss.average = my_metrics.erl.average;
268 metrics->echo_return_loss.maximum = my_metrics.erl.max;
269 metrics->echo_return_loss.minimum = my_metrics.erl.min;
270
271 metrics->echo_return_loss_enhancement.instant = my_metrics.erle.instant;
272 metrics->echo_return_loss_enhancement.average = my_metrics.erle.average;
273 metrics->echo_return_loss_enhancement.maximum = my_metrics.erle.max;
274 metrics->echo_return_loss_enhancement.minimum = my_metrics.erle.min;
275
276 metrics->a_nlp.instant = my_metrics.aNlp.instant;
277 metrics->a_nlp.average = my_metrics.aNlp.average;
278 metrics->a_nlp.maximum = my_metrics.aNlp.max;
279 metrics->a_nlp.minimum = my_metrics.aNlp.min;
280
281 return apm_->kNoError;
282}
283
284bool EchoCancellationImpl::stream_has_echo() const {
285 return stream_has_echo_;
286}
287
bjornv@google.com1ba3dbe2011-10-03 08:18:10 +0000288int EchoCancellationImpl::enable_delay_logging(bool enable) {
andrew@webrtc.org40654032012-01-30 20:51:15 +0000289 CriticalSectionScoped crit_scoped(apm_->crit());
bjornv@google.com1ba3dbe2011-10-03 08:18:10 +0000290 delay_logging_enabled_ = enable;
291 return Configure();
292}
293
294bool EchoCancellationImpl::is_delay_logging_enabled() const {
295 return delay_logging_enabled_;
296}
297
298// TODO(bjornv): How should we handle the multi-channel case?
299int EchoCancellationImpl::GetDelayMetrics(int* median, int* std) {
andrew@webrtc.org40654032012-01-30 20:51:15 +0000300 CriticalSectionScoped crit_scoped(apm_->crit());
bjornv@google.com1ba3dbe2011-10-03 08:18:10 +0000301 if (median == NULL) {
302 return apm_->kNullPointerError;
303 }
304 if (std == NULL) {
305 return apm_->kNullPointerError;
306 }
307
308 if (!is_component_enabled() || !delay_logging_enabled_) {
309 return apm_->kNotEnabledError;
310 }
311
312 Handle* my_handle = static_cast<Handle*>(handle(0));
313 if (WebRtcAec_GetDelayMetrics(my_handle, median, std) !=
314 apm_->kNoError) {
315 return GetHandleError(my_handle);
316 }
317
318 return apm_->kNoError;
319}
320
bjornv@webrtc.org91d11b32013-03-05 16:53:09 +0000321struct AecCore* EchoCancellationImpl::aec_core() const {
322 CriticalSectionScoped crit_scoped(apm_->crit());
323 if (!is_component_enabled()) {
324 return NULL;
325 }
326 Handle* my_handle = static_cast<Handle*>(handle(0));
327 return WebRtcAec_aec_core(my_handle);
328}
329
niklase@google.com470e71d2011-07-07 08:21:25 +0000330int EchoCancellationImpl::Initialize() {
331 int err = ProcessingComponent::Initialize();
332 if (err != apm_->kNoError || !is_component_enabled()) {
333 return err;
334 }
335
336 was_stream_drift_set_ = false;
337
338 return apm_->kNoError;
339}
340
niklase@google.com470e71d2011-07-07 08:21:25 +0000341void* EchoCancellationImpl::CreateHandle() const {
342 Handle* handle = NULL;
343 if (WebRtcAec_Create(&handle) != apm_->kNoError) {
344 handle = NULL;
345 } else {
346 assert(handle != NULL);
347 }
348
349 return handle;
350}
351
352int EchoCancellationImpl::DestroyHandle(void* handle) const {
353 assert(handle != NULL);
354 return WebRtcAec_Free(static_cast<Handle*>(handle));
355}
356
357int EchoCancellationImpl::InitializeHandle(void* handle) const {
358 assert(handle != NULL);
359 return WebRtcAec_Init(static_cast<Handle*>(handle),
360 apm_->sample_rate_hz(),
361 device_sample_rate_hz_);
362}
363
364int EchoCancellationImpl::ConfigureHandle(void* handle) const {
365 assert(handle != NULL);
366 AecConfig config;
367 config.metricsMode = metrics_enabled_;
368 config.nlpMode = MapSetting(suppression_level_);
369 config.skewMode = drift_compensation_enabled_;
bjornv@google.com1ba3dbe2011-10-03 08:18:10 +0000370 config.delay_logging = delay_logging_enabled_;
niklase@google.com470e71d2011-07-07 08:21:25 +0000371
niklase@google.com470e71d2011-07-07 08:21:25 +0000372 return WebRtcAec_set_config(static_cast<Handle*>(handle), config);
373}
374
375int EchoCancellationImpl::num_handles_required() const {
376 return apm_->num_output_channels() *
377 apm_->num_reverse_channels();
378}
379
380int EchoCancellationImpl::GetHandleError(void* handle) const {
381 assert(handle != NULL);
382 return MapError(WebRtcAec_get_error_code(static_cast<Handle*>(handle)));
383}
384} // namespace webrtc