1/*
2 *  Copyright (c) 2011 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 "echo_cancellation_impl.h"
12
13#include <cassert>
14#include <string.h>
15
16#include "critical_section_wrapper.h"
17#include "echo_cancellation.h"
18
19#include "audio_processing_impl.h"
20#include "audio_buffer.h"
21
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;
35    default:
36      return -1;
37  }
38}
39
40int MapError(int err) {
41  switch (err) {
42    case AEC_UNSUPPORTED_FUNCTION_ERROR:
43      return AudioProcessing::kUnsupportedFunctionError;
44      break;
45    case AEC_BAD_PARAMETER_ERROR:
46      return AudioProcessing::kBadParameterError;
47      break;
48    case AEC_BAD_PARAMETER_WARNING:
49      return AudioProcessing::kBadStreamParameterWarning;
50      break;
51    default:
52      // AEC_UNSPECIFIED_ERROR
53      // AEC_UNINITIALIZED_ERROR
54      // AEC_NULL_POINTER_ERROR
55      return AudioProcessing::kUnspecifiedError;
56  }
57}
58}  // namespace
59
60EchoCancellationImpl::EchoCancellationImpl(const AudioProcessingImpl* apm)
61  : ProcessingComponent(apm),
62    apm_(apm),
63    drift_compensation_enabled_(false),
64    metrics_enabled_(false),
65    suppression_level_(kModerateSuppression),
66    device_sample_rate_hz_(48000),
67    stream_drift_samples_(0),
68    was_stream_drift_set_(false),
69    stream_has_echo_(false),
70    delay_logging_enabled_(false) {}
71
72EchoCancellationImpl::~EchoCancellationImpl() {}
73
74int EchoCancellationImpl::ProcessRenderAudio(const AudioBuffer* audio) {
75  if (!is_component_enabled()) {
76    return apm_->kNoError;
77  }
78
79  assert(audio->samples_per_split_channel() <= 160);
80  assert(audio->num_channels() == apm_->num_reverse_channels());
81
82  int err = apm_->kNoError;
83
84  // The ordering convention must be followed to pass to the correct AEC.
85  size_t handle_index = 0;
86  for (int i = 0; i < apm_->num_output_channels(); i++) {
87    for (int j = 0; j < audio->num_channels(); j++) {
88      Handle* my_handle = static_cast<Handle*>(handle(handle_index));
89      err = WebRtcAec_BufferFarend(
90          my_handle,
91          audio->low_pass_split_data(j),
92          static_cast<WebRtc_Word16>(audio->samples_per_split_channel()));
93
94      if (err != apm_->kNoError) {
95        return GetHandleError(my_handle);  // TODO(ajm): warning possible?
96      }
97
98      handle_index++;
99    }
100  }
101
102  return apm_->kNoError;
103}
104
105int EchoCancellationImpl::ProcessCaptureAudio(AudioBuffer* audio) {
106  if (!is_component_enabled()) {
107    return apm_->kNoError;
108  }
109
110  if (!apm_->was_stream_delay_set()) {
111    return apm_->kStreamParameterNotSetError;
112  }
113
114  if (drift_compensation_enabled_ && !was_stream_drift_set_) {
115    return apm_->kStreamParameterNotSetError;
116  }
117
118  assert(audio->samples_per_split_channel() <= 160);
119  assert(audio->num_channels() == apm_->num_output_channels());
120
121  int err = apm_->kNoError;
122
123  // The ordering convention must be followed to pass to the correct AEC.
124  size_t handle_index = 0;
125  stream_has_echo_ = false;
126  for (int i = 0; i < audio->num_channels(); i++) {
127    for (int j = 0; j < apm_->num_reverse_channels(); j++) {
128      Handle* my_handle = handle(handle_index);
129      err = WebRtcAec_Process(
130          my_handle,
131          audio->low_pass_split_data(i),
132          audio->high_pass_split_data(i),
133          audio->low_pass_split_data(i),
134          audio->high_pass_split_data(i),
135          static_cast<WebRtc_Word16>(audio->samples_per_split_channel()),
136          apm_->stream_delay_ms(),
137          stream_drift_samples_);
138
139      if (err != apm_->kNoError) {
140        err = GetHandleError(my_handle);
141        // TODO(ajm): Figure out how to return warnings properly.
142        if (err != apm_->kBadStreamParameterWarning) {
143          return err;
144        }
145      }
146
147      WebRtc_Word16 status = 0;
148      err = WebRtcAec_get_echo_status(my_handle, &status);
149      if (err != apm_->kNoError) {
150        return GetHandleError(my_handle);
151      }
152
153      if (status == 1) {
154        stream_has_echo_ = true;
155      }
156
157      handle_index++;
158    }
159  }
160
161  was_stream_drift_set_ = false;
162  return apm_->kNoError;
163}
164
165int EchoCancellationImpl::Enable(bool enable) {
166  CriticalSectionScoped crit_scoped(*apm_->crit());
167  // Ensure AEC and AECM are not both enabled.
168  if (enable && apm_->echo_control_mobile()->is_enabled()) {
169    return apm_->kBadParameterError;
170  }
171
172  return EnableComponent(enable);
173}
174
175bool EchoCancellationImpl::is_enabled() const {
176  return is_component_enabled();
177}
178
179int EchoCancellationImpl::set_suppression_level(SuppressionLevel level) {
180  CriticalSectionScoped crit_scoped(*apm_->crit());
181  if (MapSetting(level) == -1) {
182    return apm_->kBadParameterError;
183  }
184
185  suppression_level_ = level;
186  return Configure();
187}
188
189EchoCancellation::SuppressionLevel EchoCancellationImpl::suppression_level()
190    const {
191  return suppression_level_;
192}
193
194int EchoCancellationImpl::enable_drift_compensation(bool enable) {
195  CriticalSectionScoped crit_scoped(*apm_->crit());
196  drift_compensation_enabled_ = enable;
197  return Configure();
198}
199
200bool EchoCancellationImpl::is_drift_compensation_enabled() const {
201  return drift_compensation_enabled_;
202}
203
204int EchoCancellationImpl::set_device_sample_rate_hz(int rate) {
205  CriticalSectionScoped crit_scoped(*apm_->crit());
206  if (rate < 8000 || rate > 96000) {
207    return apm_->kBadParameterError;
208  }
209
210  device_sample_rate_hz_ = rate;
211  return Initialize();
212}
213
214int EchoCancellationImpl::device_sample_rate_hz() const {
215  return device_sample_rate_hz_;
216}
217
218int EchoCancellationImpl::set_stream_drift_samples(int drift) {
219  was_stream_drift_set_ = true;
220  stream_drift_samples_ = drift;
221  return apm_->kNoError;
222}
223
224int EchoCancellationImpl::stream_drift_samples() const {
225  return stream_drift_samples_;
226}
227
228int EchoCancellationImpl::enable_metrics(bool enable) {
229  CriticalSectionScoped crit_scoped(*apm_->crit());
230  metrics_enabled_ = enable;
231  return Configure();
232}
233
234bool EchoCancellationImpl::are_metrics_enabled() const {
235  return metrics_enabled_;
236}
237
238// TODO(ajm): we currently just use the metrics from the first AEC. Think more
239//            aboue the best way to extend this to multi-channel.
240int EchoCancellationImpl::GetMetrics(Metrics* metrics) {
241  CriticalSectionScoped crit_scoped(*apm_->crit());
242  if (metrics == NULL) {
243    return apm_->kNullPointerError;
244  }
245
246  if (!is_component_enabled() || !metrics_enabled_) {
247    return apm_->kNotEnabledError;
248  }
249
250  AecMetrics my_metrics;
251  memset(&my_metrics, 0, sizeof(my_metrics));
252  memset(metrics, 0, sizeof(Metrics));
253
254  Handle* my_handle = static_cast<Handle*>(handle(0));
255  int err = WebRtcAec_GetMetrics(my_handle, &my_metrics);
256  if (err != apm_->kNoError) {
257    return GetHandleError(my_handle);
258  }
259
260  metrics->residual_echo_return_loss.instant = my_metrics.rerl.instant;
261  metrics->residual_echo_return_loss.average = my_metrics.rerl.average;
262  metrics->residual_echo_return_loss.maximum = my_metrics.rerl.max;
263  metrics->residual_echo_return_loss.minimum = my_metrics.rerl.min;
264
265  metrics->echo_return_loss.instant = my_metrics.erl.instant;
266  metrics->echo_return_loss.average = my_metrics.erl.average;
267  metrics->echo_return_loss.maximum = my_metrics.erl.max;
268  metrics->echo_return_loss.minimum = my_metrics.erl.min;
269
270  metrics->echo_return_loss_enhancement.instant = my_metrics.erle.instant;
271  metrics->echo_return_loss_enhancement.average = my_metrics.erle.average;
272  metrics->echo_return_loss_enhancement.maximum = my_metrics.erle.max;
273  metrics->echo_return_loss_enhancement.minimum = my_metrics.erle.min;
274
275  metrics->a_nlp.instant = my_metrics.aNlp.instant;
276  metrics->a_nlp.average = my_metrics.aNlp.average;
277  metrics->a_nlp.maximum = my_metrics.aNlp.max;
278  metrics->a_nlp.minimum = my_metrics.aNlp.min;
279
280  return apm_->kNoError;
281}
282
283bool EchoCancellationImpl::stream_has_echo() const {
284  return stream_has_echo_;
285}
286
287int EchoCancellationImpl::enable_delay_logging(bool enable) {
288  CriticalSectionScoped crit_scoped(*apm_->crit());
289  delay_logging_enabled_ = enable;
290  return Configure();
291}
292
293bool EchoCancellationImpl::is_delay_logging_enabled() const {
294  return delay_logging_enabled_;
295}
296
297// TODO(bjornv): How should we handle the multi-channel case?
298int EchoCancellationImpl::GetDelayMetrics(int* median, int* std) {
299  CriticalSectionScoped crit_scoped(*apm_->crit());
300  if (median == NULL) {
301    return apm_->kNullPointerError;
302  }
303  if (std == NULL) {
304    return apm_->kNullPointerError;
305  }
306
307  if (!is_component_enabled() || !delay_logging_enabled_) {
308    return apm_->kNotEnabledError;
309  }
310
311  Handle* my_handle = static_cast<Handle*>(handle(0));
312  if (WebRtcAec_GetDelayMetrics(my_handle, median, std) !=
313      apm_->kNoError) {
314    return GetHandleError(my_handle);
315  }
316
317  return apm_->kNoError;
318}
319
320int EchoCancellationImpl::Initialize() {
321  int err = ProcessingComponent::Initialize();
322  if (err != apm_->kNoError || !is_component_enabled()) {
323    return err;
324  }
325
326  was_stream_drift_set_ = false;
327
328  return apm_->kNoError;
329}
330
331int EchoCancellationImpl::get_version(char* version,
332                                      int version_len_bytes) const {
333  if (WebRtcAec_get_version(version, version_len_bytes) != 0) {
334      return apm_->kBadParameterError;
335  }
336
337  return apm_->kNoError;
338}
339
340void* EchoCancellationImpl::CreateHandle() const {
341  Handle* handle = NULL;
342  if (WebRtcAec_Create(&handle) != apm_->kNoError) {
343    handle = NULL;
344  } else {
345    assert(handle != NULL);
346  }
347
348  return handle;
349}
350
351int EchoCancellationImpl::DestroyHandle(void* handle) const {
352  assert(handle != NULL);
353  return WebRtcAec_Free(static_cast<Handle*>(handle));
354}
355
356int EchoCancellationImpl::InitializeHandle(void* handle) const {
357  assert(handle != NULL);
358  return WebRtcAec_Init(static_cast<Handle*>(handle),
359                       apm_->sample_rate_hz(),
360                       device_sample_rate_hz_);
361}
362
363int EchoCancellationImpl::ConfigureHandle(void* handle) const {
364  assert(handle != NULL);
365  AecConfig config;
366  config.metricsMode = metrics_enabled_;
367  config.nlpMode = MapSetting(suppression_level_);
368  config.skewMode = drift_compensation_enabled_;
369  config.delay_logging = delay_logging_enabled_;
370
371  return WebRtcAec_set_config(static_cast<Handle*>(handle), config);
372}
373
374int EchoCancellationImpl::num_handles_required() const {
375  return apm_->num_output_channels() *
376         apm_->num_reverse_channels();
377}
378
379int EchoCancellationImpl::GetHandleError(void* handle) const {
380  assert(handle != NULL);
381  return MapError(WebRtcAec_get_error_code(static_cast<Handle*>(handle)));
382}
383}  // namespace webrtc
384