blob: b968deb5c4d1170eb48a392ff7a6294645971ad8 [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 {
27WebRtc_Word16 MapSetting(EchoCancellation::SuppressionLevel level) {
28 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),
89 static_cast<WebRtc_Word16>(audio->samples_per_split_channel()));
90
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),
132 static_cast<WebRtc_Word16>(audio->samples_per_split_channel()),
133 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
215int EchoCancellationImpl::set_stream_drift_samples(int drift) {
216 was_stream_drift_set_ = true;
217 stream_drift_samples_ = drift;
218 return apm_->kNoError;
219}
220
221int EchoCancellationImpl::stream_drift_samples() const {
222 return stream_drift_samples_;
223}
224
225int EchoCancellationImpl::enable_metrics(bool enable) {
andrew@webrtc.org40654032012-01-30 20:51:15 +0000226 CriticalSectionScoped crit_scoped(apm_->crit());
niklase@google.com470e71d2011-07-07 08:21:25 +0000227 metrics_enabled_ = enable;
228 return Configure();
229}
230
231bool EchoCancellationImpl::are_metrics_enabled() const {
232 return metrics_enabled_;
233}
234
235// TODO(ajm): we currently just use the metrics from the first AEC. Think more
236// aboue the best way to extend this to multi-channel.
237int EchoCancellationImpl::GetMetrics(Metrics* metrics) {
andrew@webrtc.org40654032012-01-30 20:51:15 +0000238 CriticalSectionScoped crit_scoped(apm_->crit());
niklase@google.com470e71d2011-07-07 08:21:25 +0000239 if (metrics == NULL) {
240 return apm_->kNullPointerError;
241 }
242
243 if (!is_component_enabled() || !metrics_enabled_) {
244 return apm_->kNotEnabledError;
245 }
246
247 AecMetrics my_metrics;
248 memset(&my_metrics, 0, sizeof(my_metrics));
249 memset(metrics, 0, sizeof(Metrics));
250
251 Handle* my_handle = static_cast<Handle*>(handle(0));
252 int err = WebRtcAec_GetMetrics(my_handle, &my_metrics);
253 if (err != apm_->kNoError) {
254 return GetHandleError(my_handle);
255 }
256
257 metrics->residual_echo_return_loss.instant = my_metrics.rerl.instant;
258 metrics->residual_echo_return_loss.average = my_metrics.rerl.average;
259 metrics->residual_echo_return_loss.maximum = my_metrics.rerl.max;
260 metrics->residual_echo_return_loss.minimum = my_metrics.rerl.min;
261
262 metrics->echo_return_loss.instant = my_metrics.erl.instant;
263 metrics->echo_return_loss.average = my_metrics.erl.average;
264 metrics->echo_return_loss.maximum = my_metrics.erl.max;
265 metrics->echo_return_loss.minimum = my_metrics.erl.min;
266
267 metrics->echo_return_loss_enhancement.instant = my_metrics.erle.instant;
268 metrics->echo_return_loss_enhancement.average = my_metrics.erle.average;
269 metrics->echo_return_loss_enhancement.maximum = my_metrics.erle.max;
270 metrics->echo_return_loss_enhancement.minimum = my_metrics.erle.min;
271
272 metrics->a_nlp.instant = my_metrics.aNlp.instant;
273 metrics->a_nlp.average = my_metrics.aNlp.average;
274 metrics->a_nlp.maximum = my_metrics.aNlp.max;
275 metrics->a_nlp.minimum = my_metrics.aNlp.min;
276
277 return apm_->kNoError;
278}
279
280bool EchoCancellationImpl::stream_has_echo() const {
281 return stream_has_echo_;
282}
283
bjornv@google.com1ba3dbe2011-10-03 08:18:10 +0000284int EchoCancellationImpl::enable_delay_logging(bool enable) {
andrew@webrtc.org40654032012-01-30 20:51:15 +0000285 CriticalSectionScoped crit_scoped(apm_->crit());
bjornv@google.com1ba3dbe2011-10-03 08:18:10 +0000286 delay_logging_enabled_ = enable;
287 return Configure();
288}
289
290bool EchoCancellationImpl::is_delay_logging_enabled() const {
291 return delay_logging_enabled_;
292}
293
294// TODO(bjornv): How should we handle the multi-channel case?
295int EchoCancellationImpl::GetDelayMetrics(int* median, int* std) {
andrew@webrtc.org40654032012-01-30 20:51:15 +0000296 CriticalSectionScoped crit_scoped(apm_->crit());
bjornv@google.com1ba3dbe2011-10-03 08:18:10 +0000297 if (median == NULL) {
298 return apm_->kNullPointerError;
299 }
300 if (std == NULL) {
301 return apm_->kNullPointerError;
302 }
303
304 if (!is_component_enabled() || !delay_logging_enabled_) {
305 return apm_->kNotEnabledError;
306 }
307
308 Handle* my_handle = static_cast<Handle*>(handle(0));
309 if (WebRtcAec_GetDelayMetrics(my_handle, median, std) !=
310 apm_->kNoError) {
311 return GetHandleError(my_handle);
312 }
313
314 return apm_->kNoError;
315}
316
niklase@google.com470e71d2011-07-07 08:21:25 +0000317int EchoCancellationImpl::Initialize() {
318 int err = ProcessingComponent::Initialize();
319 if (err != apm_->kNoError || !is_component_enabled()) {
320 return err;
321 }
322
323 was_stream_drift_set_ = false;
324
325 return apm_->kNoError;
326}
327
niklase@google.com470e71d2011-07-07 08:21:25 +0000328void* EchoCancellationImpl::CreateHandle() const {
329 Handle* handle = NULL;
330 if (WebRtcAec_Create(&handle) != apm_->kNoError) {
331 handle = NULL;
332 } else {
333 assert(handle != NULL);
334 }
335
336 return handle;
337}
338
339int EchoCancellationImpl::DestroyHandle(void* handle) const {
340 assert(handle != NULL);
341 return WebRtcAec_Free(static_cast<Handle*>(handle));
342}
343
344int EchoCancellationImpl::InitializeHandle(void* handle) const {
345 assert(handle != NULL);
346 return WebRtcAec_Init(static_cast<Handle*>(handle),
347 apm_->sample_rate_hz(),
348 device_sample_rate_hz_);
349}
350
351int EchoCancellationImpl::ConfigureHandle(void* handle) const {
352 assert(handle != NULL);
353 AecConfig config;
354 config.metricsMode = metrics_enabled_;
355 config.nlpMode = MapSetting(suppression_level_);
356 config.skewMode = drift_compensation_enabled_;
bjornv@google.com1ba3dbe2011-10-03 08:18:10 +0000357 config.delay_logging = delay_logging_enabled_;
niklase@google.com470e71d2011-07-07 08:21:25 +0000358
359 return WebRtcAec_set_config(static_cast<Handle*>(handle), config);
360}
361
362int EchoCancellationImpl::num_handles_required() const {
363 return apm_->num_output_channels() *
364 apm_->num_reverse_channels();
365}
366
367int EchoCancellationImpl::GetHandleError(void* handle) const {
368 assert(handle != NULL);
369 return MapError(WebRtcAec_get_error_code(static_cast<Handle*>(handle)));
370}
371} // namespace webrtc