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 21 namespace webrtc { 22 23 typedef void Handle; 24 25 namespace { 26 int16_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 43 AudioProcessing::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 61 size_t EchoControlMobile::echo_path_size_bytes() { 62 return WebRtcAecm_echo_path_size_bytes(); 63 } 64 65 EchoControlMobileImpl::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 74 EchoControlMobileImpl::~EchoControlMobileImpl() { 75 if (external_echo_path_ != NULL) { 76 delete [] external_echo_path_; 77 external_echo_path_ = NULL; 78 } 79 } 80 81 int 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 112 int 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 158 int 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 168 bool EchoControlMobileImpl::is_enabled() const { 169 return is_component_enabled(); 170 } 171 172 int 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 182 EchoControlMobile::RoutingMode EchoControlMobileImpl::routing_mode() 183 const { 184 return routing_mode_; 185 } 186 187 int EchoControlMobileImpl::enable_comfort_noise(bool enable) { 188 CriticalSectionScoped crit_scoped(crit_); 189 comfort_noise_enabled_ = enable; 190 return Configure(); 191 } 192 193 bool EchoControlMobileImpl::is_comfort_noise_enabled() const { 194 return comfort_noise_enabled_; 195 } 196 197 int 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 216 int 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 239 int 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 252 void* 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 263 void EchoControlMobileImpl::DestroyHandle(void* handle) const { 264 WebRtcAecm_Free(static_cast<Handle*>(handle)); 265 } 266 267 int 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 284 int 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 292 int EchoControlMobileImpl::num_handles_required() const { 293 return apm_->num_output_channels() * 294 apm_->num_reverse_channels(); 295 } 296 297 int 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