blob: a32fb3364f9b8380229f85a318e5c95289295f94 [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
bjornv@webrtc.org21a2fc92013-02-15 17:01:03 +000020#include "webrtc/modules/audio_processing/aec/include/echo_cancellation.h"
niklase@google.com470e71d2011-07-07 08:21:25 +000021
22namespace 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
57EchoCancellationImpl::EchoCancellationImpl(const AudioProcessingImpl* apm)
58 : ProcessingComponent(apm),
59 apm_(apm),
60 drift_compensation_enabled_(false),
61 metrics_enabled_(false),
62 suppression_level_(kModerateSuppression),
63 device_sample_rate_hz_(48000),
64 stream_drift_samples_(0),
65 was_stream_drift_set_(false),
bjornv@google.com1ba3dbe2011-10-03 08:18:10 +000066 stream_has_echo_(false),
67 delay_logging_enabled_(false) {}
niklase@google.com470e71d2011-07-07 08:21:25 +000068
69EchoCancellationImpl::~EchoCancellationImpl() {}
70
71int EchoCancellationImpl::ProcessRenderAudio(const AudioBuffer* audio) {
72 if (!is_component_enabled()) {
73 return apm_->kNoError;
74 }
75
76 assert(audio->samples_per_split_channel() <= 160);
77 assert(audio->num_channels() == apm_->num_reverse_channels());
78
79 int err = apm_->kNoError;
80
81 // The ordering convention must be followed to pass to the correct AEC.
82 size_t handle_index = 0;
83 for (int i = 0; i < apm_->num_output_channels(); i++) {
84 for (int j = 0; j < audio->num_channels(); j++) {
85 Handle* my_handle = static_cast<Handle*>(handle(handle_index));
86 err = WebRtcAec_BufferFarend(
87 my_handle,
88 audio->low_pass_split_data(j),
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +000089 static_cast<int16_t>(audio->samples_per_split_channel()));
niklase@google.com470e71d2011-07-07 08:21:25 +000090
91 if (err != apm_->kNoError) {
92 return GetHandleError(my_handle); // TODO(ajm): warning possible?
93 }
94
95 handle_index++;
96 }
97 }
98
99 return apm_->kNoError;
100}
101
102int EchoCancellationImpl::ProcessCaptureAudio(AudioBuffer* audio) {
103 if (!is_component_enabled()) {
104 return apm_->kNoError;
105 }
106
107 if (!apm_->was_stream_delay_set()) {
108 return apm_->kStreamParameterNotSetError;
109 }
110
111 if (drift_compensation_enabled_ && !was_stream_drift_set_) {
112 return apm_->kStreamParameterNotSetError;
113 }
114
115 assert(audio->samples_per_split_channel() <= 160);
116 assert(audio->num_channels() == apm_->num_output_channels());
117
118 int err = apm_->kNoError;
119
120 // The ordering convention must be followed to pass to the correct AEC.
121 size_t handle_index = 0;
122 stream_has_echo_ = false;
123 for (int i = 0; i < audio->num_channels(); i++) {
124 for (int j = 0; j < apm_->num_reverse_channels(); j++) {
125 Handle* my_handle = handle(handle_index);
126 err = WebRtcAec_Process(
127 my_handle,
128 audio->low_pass_split_data(i),
129 audio->high_pass_split_data(i),
130 audio->low_pass_split_data(i),
131 audio->high_pass_split_data(i),
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000132 static_cast<int16_t>(audio->samples_per_split_channel()),
niklase@google.com470e71d2011-07-07 08:21:25 +0000133 apm_->stream_delay_ms(),
134 stream_drift_samples_);
135
136 if (err != apm_->kNoError) {
137 err = GetHandleError(my_handle);
138 // TODO(ajm): Figure out how to return warnings properly.
139 if (err != apm_->kBadStreamParameterWarning) {
140 return err;
141 }
142 }
143
bjornv@webrtc.org21a2fc92013-02-15 17:01:03 +0000144 int status = 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000145 err = WebRtcAec_get_echo_status(my_handle, &status);
146 if (err != apm_->kNoError) {
147 return GetHandleError(my_handle);
148 }
149
150 if (status == 1) {
151 stream_has_echo_ = true;
152 }
153
154 handle_index++;
155 }
156 }
157
158 was_stream_drift_set_ = false;
159 return apm_->kNoError;
160}
161
162int EchoCancellationImpl::Enable(bool enable) {
andrew@webrtc.org40654032012-01-30 20:51:15 +0000163 CriticalSectionScoped crit_scoped(apm_->crit());
niklase@google.com470e71d2011-07-07 08:21:25 +0000164 // Ensure AEC and AECM are not both enabled.
165 if (enable && apm_->echo_control_mobile()->is_enabled()) {
166 return apm_->kBadParameterError;
167 }
168
169 return EnableComponent(enable);
170}
171
172bool EchoCancellationImpl::is_enabled() const {
173 return is_component_enabled();
174}
175
176int EchoCancellationImpl::set_suppression_level(SuppressionLevel level) {
andrew@webrtc.org40654032012-01-30 20:51:15 +0000177 CriticalSectionScoped crit_scoped(apm_->crit());
niklase@google.com470e71d2011-07-07 08:21:25 +0000178 if (MapSetting(level) == -1) {
179 return apm_->kBadParameterError;
180 }
181
182 suppression_level_ = level;
183 return Configure();
184}
185
186EchoCancellation::SuppressionLevel EchoCancellationImpl::suppression_level()
187 const {
188 return suppression_level_;
189}
190
191int EchoCancellationImpl::enable_drift_compensation(bool enable) {
andrew@webrtc.org40654032012-01-30 20:51:15 +0000192 CriticalSectionScoped crit_scoped(apm_->crit());
niklase@google.com470e71d2011-07-07 08:21:25 +0000193 drift_compensation_enabled_ = enable;
194 return Configure();
195}
196
197bool EchoCancellationImpl::is_drift_compensation_enabled() const {
198 return drift_compensation_enabled_;
199}
200
201int EchoCancellationImpl::set_device_sample_rate_hz(int rate) {
andrew@webrtc.org40654032012-01-30 20:51:15 +0000202 CriticalSectionScoped crit_scoped(apm_->crit());
niklase@google.com470e71d2011-07-07 08:21:25 +0000203 if (rate < 8000 || rate > 96000) {
204 return apm_->kBadParameterError;
205 }
206
207 device_sample_rate_hz_ = rate;
208 return Initialize();
209}
210
211int EchoCancellationImpl::device_sample_rate_hz() const {
212 return device_sample_rate_hz_;
213}
214
andrew@webrtc.org6be1e932013-03-01 18:47:28 +0000215void EchoCancellationImpl::set_stream_drift_samples(int drift) {
niklase@google.com470e71d2011-07-07 08:21:25 +0000216 was_stream_drift_set_ = true;
217 stream_drift_samples_ = drift;
niklase@google.com470e71d2011-07-07 08:21:25 +0000218}
219
220int EchoCancellationImpl::stream_drift_samples() const {
221 return stream_drift_samples_;
222}
223
224int EchoCancellationImpl::enable_metrics(bool enable) {
andrew@webrtc.org40654032012-01-30 20:51:15 +0000225 CriticalSectionScoped crit_scoped(apm_->crit());
niklase@google.com470e71d2011-07-07 08:21:25 +0000226 metrics_enabled_ = enable;
227 return Configure();
228}
229
230bool EchoCancellationImpl::are_metrics_enabled() const {
231 return metrics_enabled_;
232}
233
234// TODO(ajm): we currently just use the metrics from the first AEC. Think more
235// aboue the best way to extend this to multi-channel.
236int EchoCancellationImpl::GetMetrics(Metrics* metrics) {
andrew@webrtc.org40654032012-01-30 20:51:15 +0000237 CriticalSectionScoped crit_scoped(apm_->crit());
niklase@google.com470e71d2011-07-07 08:21:25 +0000238 if (metrics == NULL) {
239 return apm_->kNullPointerError;
240 }
241
242 if (!is_component_enabled() || !metrics_enabled_) {
243 return apm_->kNotEnabledError;
244 }
245
246 AecMetrics my_metrics;
247 memset(&my_metrics, 0, sizeof(my_metrics));
248 memset(metrics, 0, sizeof(Metrics));
249
250 Handle* my_handle = static_cast<Handle*>(handle(0));
251 int err = WebRtcAec_GetMetrics(my_handle, &my_metrics);
252 if (err != apm_->kNoError) {
253 return GetHandleError(my_handle);
254 }
255
256 metrics->residual_echo_return_loss.instant = my_metrics.rerl.instant;
257 metrics->residual_echo_return_loss.average = my_metrics.rerl.average;
258 metrics->residual_echo_return_loss.maximum = my_metrics.rerl.max;
259 metrics->residual_echo_return_loss.minimum = my_metrics.rerl.min;
260
261 metrics->echo_return_loss.instant = my_metrics.erl.instant;
262 metrics->echo_return_loss.average = my_metrics.erl.average;
263 metrics->echo_return_loss.maximum = my_metrics.erl.max;
264 metrics->echo_return_loss.minimum = my_metrics.erl.min;
265
266 metrics->echo_return_loss_enhancement.instant = my_metrics.erle.instant;
267 metrics->echo_return_loss_enhancement.average = my_metrics.erle.average;
268 metrics->echo_return_loss_enhancement.maximum = my_metrics.erle.max;
269 metrics->echo_return_loss_enhancement.minimum = my_metrics.erle.min;
270
271 metrics->a_nlp.instant = my_metrics.aNlp.instant;
272 metrics->a_nlp.average = my_metrics.aNlp.average;
273 metrics->a_nlp.maximum = my_metrics.aNlp.max;
274 metrics->a_nlp.minimum = my_metrics.aNlp.min;
275
276 return apm_->kNoError;
277}
278
279bool EchoCancellationImpl::stream_has_echo() const {
280 return stream_has_echo_;
281}
282
bjornv@google.com1ba3dbe2011-10-03 08:18:10 +0000283int EchoCancellationImpl::enable_delay_logging(bool enable) {
andrew@webrtc.org40654032012-01-30 20:51:15 +0000284 CriticalSectionScoped crit_scoped(apm_->crit());
bjornv@google.com1ba3dbe2011-10-03 08:18:10 +0000285 delay_logging_enabled_ = enable;
286 return Configure();
287}
288
289bool EchoCancellationImpl::is_delay_logging_enabled() const {
290 return delay_logging_enabled_;
291}
292
293// TODO(bjornv): How should we handle the multi-channel case?
294int EchoCancellationImpl::GetDelayMetrics(int* median, int* std) {
andrew@webrtc.org40654032012-01-30 20:51:15 +0000295 CriticalSectionScoped crit_scoped(apm_->crit());
bjornv@google.com1ba3dbe2011-10-03 08:18:10 +0000296 if (median == NULL) {
297 return apm_->kNullPointerError;
298 }
299 if (std == NULL) {
300 return apm_->kNullPointerError;
301 }
302
303 if (!is_component_enabled() || !delay_logging_enabled_) {
304 return apm_->kNotEnabledError;
305 }
306
307 Handle* my_handle = static_cast<Handle*>(handle(0));
308 if (WebRtcAec_GetDelayMetrics(my_handle, median, std) !=
309 apm_->kNoError) {
310 return GetHandleError(my_handle);
311 }
312
313 return apm_->kNoError;
314}
315
bjornv@webrtc.org91d11b32013-03-05 16:53:09 +0000316struct AecCore* EchoCancellationImpl::aec_core() const {
317 CriticalSectionScoped crit_scoped(apm_->crit());
318 if (!is_component_enabled()) {
319 return NULL;
320 }
321 Handle* my_handle = static_cast<Handle*>(handle(0));
322 return WebRtcAec_aec_core(my_handle);
323}
324
niklase@google.com470e71d2011-07-07 08:21:25 +0000325int EchoCancellationImpl::Initialize() {
326 int err = ProcessingComponent::Initialize();
327 if (err != apm_->kNoError || !is_component_enabled()) {
328 return err;
329 }
330
331 was_stream_drift_set_ = false;
332
333 return apm_->kNoError;
334}
335
niklase@google.com470e71d2011-07-07 08:21:25 +0000336void* EchoCancellationImpl::CreateHandle() const {
337 Handle* handle = NULL;
338 if (WebRtcAec_Create(&handle) != apm_->kNoError) {
339 handle = NULL;
340 } else {
341 assert(handle != NULL);
342 }
343
344 return handle;
345}
346
347int EchoCancellationImpl::DestroyHandle(void* handle) const {
348 assert(handle != NULL);
349 return WebRtcAec_Free(static_cast<Handle*>(handle));
350}
351
352int EchoCancellationImpl::InitializeHandle(void* handle) const {
353 assert(handle != NULL);
354 return WebRtcAec_Init(static_cast<Handle*>(handle),
355 apm_->sample_rate_hz(),
356 device_sample_rate_hz_);
357}
358
359int EchoCancellationImpl::ConfigureHandle(void* handle) const {
360 assert(handle != NULL);
361 AecConfig config;
362 config.metricsMode = metrics_enabled_;
363 config.nlpMode = MapSetting(suppression_level_);
364 config.skewMode = drift_compensation_enabled_;
bjornv@google.com1ba3dbe2011-10-03 08:18:10 +0000365 config.delay_logging = delay_logging_enabled_;
niklase@google.com470e71d2011-07-07 08:21:25 +0000366
367 return WebRtcAec_set_config(static_cast<Handle*>(handle), config);
368}
369
370int EchoCancellationImpl::num_handles_required() const {
371 return apm_->num_output_channels() *
372 apm_->num_reverse_channels();
373}
374
375int EchoCancellationImpl::GetHandleError(void* handle) const {
376 assert(handle != NULL);
377 return MapError(WebRtcAec_get_error_code(static_cast<Handle*>(handle)));
378}
379} // namespace webrtc