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