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_control_mobile_impl.h" 12 13#include <assert.h> 14#include <string.h> 15 16#include "webrtc/modules/audio_processing/aecm/include/echo_control_mobile.h" 17#include "webrtc/modules/audio_processing/audio_buffer.h" 18#include "webrtc/system_wrappers/interface/critical_section_wrapper.h" 19#include "webrtc/system_wrappers/interface/logging.h" 20 21namespace webrtc { 22 23typedef void Handle; 24 25namespace { 26int16_t MapSetting(EchoControlMobile::RoutingMode mode) { 27 switch (mode) { 28 case EchoControlMobile::kQuietEarpieceOrHeadset: 29 return 0; 30 case EchoControlMobile::kEarpiece: 31 return 1; 32 case EchoControlMobile::kLoudEarpiece: 33 return 2; 34 case EchoControlMobile::kSpeakerphone: 35 return 3; 36 case EchoControlMobile::kLoudSpeakerphone: 37 return 4; 38 } 39 assert(false); 40 return -1; 41} 42 43AudioProcessing::Error MapError(int err) { 44 switch (err) { 45 case AECM_UNSUPPORTED_FUNCTION_ERROR: 46 return AudioProcessing::kUnsupportedFunctionError; 47 case AECM_NULL_POINTER_ERROR: 48 return AudioProcessing::kNullPointerError; 49 case AECM_BAD_PARAMETER_ERROR: 50 return AudioProcessing::kBadParameterError; 51 case AECM_BAD_PARAMETER_WARNING: 52 return AudioProcessing::kBadStreamParameterWarning; 53 default: 54 // AECM_UNSPECIFIED_ERROR 55 // AECM_UNINITIALIZED_ERROR 56 return AudioProcessing::kUnspecifiedError; 57 } 58} 59} // namespace 60 61size_t EchoControlMobile::echo_path_size_bytes() { 62 return WebRtcAecm_echo_path_size_bytes(); 63} 64 65EchoControlMobileImpl::EchoControlMobileImpl(const AudioProcessing* apm, 66 CriticalSectionWrapper* crit) 67 : ProcessingComponent(), 68 apm_(apm), 69 crit_(crit), 70 routing_mode_(kSpeakerphone), 71 comfort_noise_enabled_(true), 72 external_echo_path_(NULL) {} 73 74EchoControlMobileImpl::~EchoControlMobileImpl() { 75 if (external_echo_path_ != NULL) { 76 delete [] external_echo_path_; 77 external_echo_path_ = NULL; 78 } 79} 80 81int EchoControlMobileImpl::ProcessRenderAudio(const AudioBuffer* audio) { 82 if (!is_component_enabled()) { 83 return apm_->kNoError; 84 } 85 86 assert(audio->samples_per_split_channel() <= 160); 87 assert(audio->num_channels() == apm_->num_reverse_channels()); 88 89 int err = apm_->kNoError; 90 91 // The ordering convention must be followed to pass to the correct AECM. 92 size_t handle_index = 0; 93 for (int i = 0; i < apm_->num_output_channels(); i++) { 94 for (int j = 0; j < audio->num_channels(); j++) { 95 Handle* my_handle = static_cast<Handle*>(handle(handle_index)); 96 err = WebRtcAecm_BufferFarend( 97 my_handle, 98 audio->low_pass_split_data(j), 99 static_cast<int16_t>(audio->samples_per_split_channel())); 100 101 if (err != apm_->kNoError) { 102 return GetHandleError(my_handle); // TODO(ajm): warning possible? 103 } 104 105 handle_index++; 106 } 107 } 108 109 return apm_->kNoError; 110} 111 112int EchoControlMobileImpl::ProcessCaptureAudio(AudioBuffer* audio) { 113 if (!is_component_enabled()) { 114 return apm_->kNoError; 115 } 116 117 if (!apm_->was_stream_delay_set()) { 118 return apm_->kStreamParameterNotSetError; 119 } 120 121 assert(audio->samples_per_split_channel() <= 160); 122 assert(audio->num_channels() == apm_->num_output_channels()); 123 124 int err = apm_->kNoError; 125 126 // The ordering convention must be followed to pass to the correct AECM. 127 size_t handle_index = 0; 128 for (int i = 0; i < audio->num_channels(); i++) { 129 // TODO(ajm): improve how this works, possibly inside AECM. 130 // This is kind of hacked up. 131 const int16_t* noisy = audio->low_pass_reference(i); 132 int16_t* clean = audio->low_pass_split_data(i); 133 if (noisy == NULL) { 134 noisy = clean; 135 clean = NULL; 136 } 137 for (int j = 0; j < apm_->num_reverse_channels(); j++) { 138 Handle* my_handle = static_cast<Handle*>(handle(handle_index)); 139 err = WebRtcAecm_Process( 140 my_handle, 141 noisy, 142 clean, 143 audio->low_pass_split_data(i), 144 static_cast<int16_t>(audio->samples_per_split_channel()), 145 apm_->stream_delay_ms()); 146 147 if (err != apm_->kNoError) { 148 return GetHandleError(my_handle); // TODO(ajm): warning possible? 149 } 150 151 handle_index++; 152 } 153 } 154 155 return apm_->kNoError; 156} 157 158int EchoControlMobileImpl::Enable(bool enable) { 159 CriticalSectionScoped crit_scoped(crit_); 160 // Ensure AEC and AECM are not both enabled. 161 if (enable && apm_->echo_cancellation()->is_enabled()) { 162 return apm_->kBadParameterError; 163 } 164 165 return EnableComponent(enable); 166} 167 168bool EchoControlMobileImpl::is_enabled() const { 169 return is_component_enabled(); 170} 171 172int EchoControlMobileImpl::set_routing_mode(RoutingMode mode) { 173 CriticalSectionScoped crit_scoped(crit_); 174 if (MapSetting(mode) == -1) { 175 return apm_->kBadParameterError; 176 } 177 178 routing_mode_ = mode; 179 return Configure(); 180} 181 182EchoControlMobile::RoutingMode EchoControlMobileImpl::routing_mode() 183 const { 184 return routing_mode_; 185} 186 187int EchoControlMobileImpl::enable_comfort_noise(bool enable) { 188 CriticalSectionScoped crit_scoped(crit_); 189 comfort_noise_enabled_ = enable; 190 return Configure(); 191} 192 193bool EchoControlMobileImpl::is_comfort_noise_enabled() const { 194 return comfort_noise_enabled_; 195} 196 197int EchoControlMobileImpl::SetEchoPath(const void* echo_path, 198 size_t size_bytes) { 199 CriticalSectionScoped crit_scoped(crit_); 200 if (echo_path == NULL) { 201 return apm_->kNullPointerError; 202 } 203 if (size_bytes != echo_path_size_bytes()) { 204 // Size mismatch 205 return apm_->kBadParameterError; 206 } 207 208 if (external_echo_path_ == NULL) { 209 external_echo_path_ = new unsigned char[size_bytes]; 210 } 211 memcpy(external_echo_path_, echo_path, size_bytes); 212 213 return Initialize(); 214} 215 216int EchoControlMobileImpl::GetEchoPath(void* echo_path, 217 size_t size_bytes) const { 218 CriticalSectionScoped crit_scoped(crit_); 219 if (echo_path == NULL) { 220 return apm_->kNullPointerError; 221 } 222 if (size_bytes != echo_path_size_bytes()) { 223 // Size mismatch 224 return apm_->kBadParameterError; 225 } 226 if (!is_component_enabled()) { 227 return apm_->kNotEnabledError; 228 } 229 230 // Get the echo path from the first channel 231 Handle* my_handle = static_cast<Handle*>(handle(0)); 232 if (WebRtcAecm_GetEchoPath(my_handle, echo_path, size_bytes) != 0) { 233 return GetHandleError(my_handle); 234 } 235 236 return apm_->kNoError; 237} 238 239int EchoControlMobileImpl::Initialize() { 240 if (!is_component_enabled()) { 241 return apm_->kNoError; 242 } 243 244 if (apm_->proc_sample_rate_hz() > apm_->kSampleRate16kHz) { 245 LOG(LS_ERROR) << "AECM only supports 16 kHz or lower sample rates"; 246 return apm_->kBadSampleRateError; 247 } 248 249 return ProcessingComponent::Initialize(); 250} 251 252void* EchoControlMobileImpl::CreateHandle() const { 253 Handle* handle = NULL; 254 if (WebRtcAecm_Create(&handle) != apm_->kNoError) { 255 handle = NULL; 256 } else { 257 assert(handle != NULL); 258 } 259 260 return handle; 261} 262 263void EchoControlMobileImpl::DestroyHandle(void* handle) const { 264 WebRtcAecm_Free(static_cast<Handle*>(handle)); 265} 266 267int EchoControlMobileImpl::InitializeHandle(void* handle) const { 268 assert(handle != NULL); 269 Handle* my_handle = static_cast<Handle*>(handle); 270 if (WebRtcAecm_Init(my_handle, apm_->proc_sample_rate_hz()) != 0) { 271 return GetHandleError(my_handle); 272 } 273 if (external_echo_path_ != NULL) { 274 if (WebRtcAecm_InitEchoPath(my_handle, 275 external_echo_path_, 276 echo_path_size_bytes()) != 0) { 277 return GetHandleError(my_handle); 278 } 279 } 280 281 return apm_->kNoError; 282} 283 284int EchoControlMobileImpl::ConfigureHandle(void* handle) const { 285 AecmConfig config; 286 config.cngMode = comfort_noise_enabled_; 287 config.echoMode = MapSetting(routing_mode_); 288 289 return WebRtcAecm_set_config(static_cast<Handle*>(handle), config); 290} 291 292int EchoControlMobileImpl::num_handles_required() const { 293 return apm_->num_output_channels() * 294 apm_->num_reverse_channels(); 295} 296 297int EchoControlMobileImpl::GetHandleError(void* handle) const { 298 assert(handle != NULL); 299 return MapError(WebRtcAecm_get_error_code(static_cast<Handle*>(handle))); 300} 301} // namespace webrtc 302