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