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/echo_control_mobile.h" 17#include "webrtc/modules/audio_processing/audio_buffer.h" 18#include "webrtc/system_wrappers/include/logging.h" 19 20namespace webrtc { 21 22typedef void Handle; 23 24namespace { 25int16_t MapSetting(EchoControlMobile::RoutingMode mode) { 26 switch (mode) { 27 case EchoControlMobile::kQuietEarpieceOrHeadset: 28 return 0; 29 case EchoControlMobile::kEarpiece: 30 return 1; 31 case EchoControlMobile::kLoudEarpiece: 32 return 2; 33 case EchoControlMobile::kSpeakerphone: 34 return 3; 35 case EchoControlMobile::kLoudSpeakerphone: 36 return 4; 37 } 38 assert(false); 39 return -1; 40} 41 42AudioProcessing::Error MapError(int err) { 43 switch (err) { 44 case AECM_UNSUPPORTED_FUNCTION_ERROR: 45 return AudioProcessing::kUnsupportedFunctionError; 46 case AECM_NULL_POINTER_ERROR: 47 return AudioProcessing::kNullPointerError; 48 case AECM_BAD_PARAMETER_ERROR: 49 return AudioProcessing::kBadParameterError; 50 case AECM_BAD_PARAMETER_WARNING: 51 return AudioProcessing::kBadStreamParameterWarning; 52 default: 53 // AECM_UNSPECIFIED_ERROR 54 // AECM_UNINITIALIZED_ERROR 55 return AudioProcessing::kUnspecifiedError; 56 } 57} 58// Maximum length that a frame of samples can have. 59static const size_t kMaxAllowedValuesOfSamplesPerFrame = 160; 60// Maximum number of frames to buffer in the render queue. 61// TODO(peah): Decrease this once we properly handle hugely unbalanced 62// reverse and forward call numbers. 63static const size_t kMaxNumFramesToBuffer = 100; 64} // namespace 65 66size_t EchoControlMobile::echo_path_size_bytes() { 67 return WebRtcAecm_echo_path_size_bytes(); 68} 69 70EchoControlMobileImpl::EchoControlMobileImpl(const AudioProcessing* apm, 71 rtc::CriticalSection* crit_render, 72 rtc::CriticalSection* crit_capture) 73 : ProcessingComponent(), 74 apm_(apm), 75 crit_render_(crit_render), 76 crit_capture_(crit_capture), 77 routing_mode_(kSpeakerphone), 78 comfort_noise_enabled_(true), 79 external_echo_path_(NULL), 80 render_queue_element_max_size_(0) { 81 RTC_DCHECK(apm); 82 RTC_DCHECK(crit_render); 83 RTC_DCHECK(crit_capture); 84} 85 86EchoControlMobileImpl::~EchoControlMobileImpl() { 87 if (external_echo_path_ != NULL) { 88 delete [] external_echo_path_; 89 external_echo_path_ = NULL; 90 } 91} 92 93int EchoControlMobileImpl::ProcessRenderAudio(const AudioBuffer* audio) { 94 rtc::CritScope cs_render(crit_render_); 95 96 if (!is_component_enabled()) { 97 return AudioProcessing::kNoError; 98 } 99 100 assert(audio->num_frames_per_band() <= 160); 101 assert(audio->num_channels() == apm_->num_reverse_channels()); 102 103 int err = AudioProcessing::kNoError; 104 // The ordering convention must be followed to pass to the correct AECM. 105 size_t handle_index = 0; 106 render_queue_buffer_.clear(); 107 for (size_t i = 0; i < apm_->num_output_channels(); i++) { 108 for (size_t j = 0; j < audio->num_channels(); j++) { 109 Handle* my_handle = static_cast<Handle*>(handle(handle_index)); 110 err = WebRtcAecm_GetBufferFarendError( 111 my_handle, audio->split_bands_const(j)[kBand0To8kHz], 112 audio->num_frames_per_band()); 113 114 if (err != AudioProcessing::kNoError) 115 return MapError(err); // TODO(ajm): warning possible?); 116 117 // Buffer the samples in the render queue. 118 render_queue_buffer_.insert(render_queue_buffer_.end(), 119 audio->split_bands_const(j)[kBand0To8kHz], 120 (audio->split_bands_const(j)[kBand0To8kHz] + 121 audio->num_frames_per_band())); 122 123 handle_index++; 124 } 125 } 126 127 // Insert the samples into the queue. 128 if (!render_signal_queue_->Insert(&render_queue_buffer_)) { 129 // The data queue is full and needs to be emptied. 130 ReadQueuedRenderData(); 131 132 // Retry the insert (should always work). 133 RTC_DCHECK_EQ(render_signal_queue_->Insert(&render_queue_buffer_), true); 134 } 135 136 return AudioProcessing::kNoError; 137} 138 139// Read chunks of data that were received and queued on the render side from 140// a queue. All the data chunks are buffered into the farend signal of the AEC. 141void EchoControlMobileImpl::ReadQueuedRenderData() { 142 rtc::CritScope cs_capture(crit_capture_); 143 144 if (!is_component_enabled()) { 145 return; 146 } 147 148 while (render_signal_queue_->Remove(&capture_queue_buffer_)) { 149 size_t handle_index = 0; 150 size_t buffer_index = 0; 151 const size_t num_frames_per_band = 152 capture_queue_buffer_.size() / 153 (apm_->num_output_channels() * apm_->num_reverse_channels()); 154 for (size_t i = 0; i < apm_->num_output_channels(); i++) { 155 for (size_t j = 0; j < apm_->num_reverse_channels(); j++) { 156 Handle* my_handle = static_cast<Handle*>(handle(handle_index)); 157 WebRtcAecm_BufferFarend(my_handle, &capture_queue_buffer_[buffer_index], 158 num_frames_per_band); 159 160 buffer_index += num_frames_per_band; 161 handle_index++; 162 } 163 } 164 } 165} 166 167int EchoControlMobileImpl::ProcessCaptureAudio(AudioBuffer* audio) { 168 rtc::CritScope cs_capture(crit_capture_); 169 170 if (!is_component_enabled()) { 171 return AudioProcessing::kNoError; 172 } 173 174 if (!apm_->was_stream_delay_set()) { 175 return AudioProcessing::kStreamParameterNotSetError; 176 } 177 178 assert(audio->num_frames_per_band() <= 160); 179 assert(audio->num_channels() == apm_->num_output_channels()); 180 181 int err = AudioProcessing::kNoError; 182 183 // The ordering convention must be followed to pass to the correct AECM. 184 size_t handle_index = 0; 185 for (size_t i = 0; i < audio->num_channels(); i++) { 186 // TODO(ajm): improve how this works, possibly inside AECM. 187 // This is kind of hacked up. 188 const int16_t* noisy = audio->low_pass_reference(i); 189 const int16_t* clean = audio->split_bands_const(i)[kBand0To8kHz]; 190 if (noisy == NULL) { 191 noisy = clean; 192 clean = NULL; 193 } 194 for (size_t j = 0; j < apm_->num_reverse_channels(); j++) { 195 Handle* my_handle = static_cast<Handle*>(handle(handle_index)); 196 err = WebRtcAecm_Process( 197 my_handle, 198 noisy, 199 clean, 200 audio->split_bands(i)[kBand0To8kHz], 201 audio->num_frames_per_band(), 202 apm_->stream_delay_ms()); 203 204 if (err != AudioProcessing::kNoError) 205 return MapError(err); 206 207 handle_index++; 208 } 209 } 210 211 return AudioProcessing::kNoError; 212} 213 214int EchoControlMobileImpl::Enable(bool enable) { 215 // Ensure AEC and AECM are not both enabled. 216 rtc::CritScope cs_render(crit_render_); 217 rtc::CritScope cs_capture(crit_capture_); 218 // The is_enabled call is safe from a deadlock perspective 219 // as both locks are allready held in the correct order. 220 if (enable && apm_->echo_cancellation()->is_enabled()) { 221 return AudioProcessing::kBadParameterError; 222 } 223 224 return EnableComponent(enable); 225} 226 227bool EchoControlMobileImpl::is_enabled() const { 228 rtc::CritScope cs(crit_capture_); 229 return is_component_enabled(); 230} 231 232int EchoControlMobileImpl::set_routing_mode(RoutingMode mode) { 233 if (MapSetting(mode) == -1) { 234 return AudioProcessing::kBadParameterError; 235 } 236 237 { 238 rtc::CritScope cs(crit_capture_); 239 routing_mode_ = mode; 240 } 241 return Configure(); 242} 243 244EchoControlMobile::RoutingMode EchoControlMobileImpl::routing_mode() 245 const { 246 rtc::CritScope cs(crit_capture_); 247 return routing_mode_; 248} 249 250int EchoControlMobileImpl::enable_comfort_noise(bool enable) { 251 { 252 rtc::CritScope cs(crit_capture_); 253 comfort_noise_enabled_ = enable; 254 } 255 return Configure(); 256} 257 258bool EchoControlMobileImpl::is_comfort_noise_enabled() const { 259 rtc::CritScope cs(crit_capture_); 260 return comfort_noise_enabled_; 261} 262 263int EchoControlMobileImpl::SetEchoPath(const void* echo_path, 264 size_t size_bytes) { 265 { 266 rtc::CritScope cs_render(crit_render_); 267 rtc::CritScope cs_capture(crit_capture_); 268 if (echo_path == NULL) { 269 return AudioProcessing::kNullPointerError; 270 } 271 if (size_bytes != echo_path_size_bytes()) { 272 // Size mismatch 273 return AudioProcessing::kBadParameterError; 274 } 275 276 if (external_echo_path_ == NULL) { 277 external_echo_path_ = new unsigned char[size_bytes]; 278 } 279 memcpy(external_echo_path_, echo_path, size_bytes); 280 } 281 282 return Initialize(); 283} 284 285int EchoControlMobileImpl::GetEchoPath(void* echo_path, 286 size_t size_bytes) const { 287 rtc::CritScope cs(crit_capture_); 288 if (echo_path == NULL) { 289 return AudioProcessing::kNullPointerError; 290 } 291 if (size_bytes != echo_path_size_bytes()) { 292 // Size mismatch 293 return AudioProcessing::kBadParameterError; 294 } 295 if (!is_component_enabled()) { 296 return AudioProcessing::kNotEnabledError; 297 } 298 299 // Get the echo path from the first channel 300 Handle* my_handle = static_cast<Handle*>(handle(0)); 301 int32_t err = WebRtcAecm_GetEchoPath(my_handle, echo_path, size_bytes); 302 if (err != 0) 303 return MapError(err); 304 305 return AudioProcessing::kNoError; 306} 307 308int EchoControlMobileImpl::Initialize() { 309 { 310 rtc::CritScope cs_capture(crit_capture_); 311 if (!is_component_enabled()) { 312 return AudioProcessing::kNoError; 313 } 314 } 315 316 if (apm_->proc_sample_rate_hz() > AudioProcessing::kSampleRate16kHz) { 317 LOG(LS_ERROR) << "AECM only supports 16 kHz or lower sample rates"; 318 return AudioProcessing::kBadSampleRateError; 319 } 320 321 int err = ProcessingComponent::Initialize(); 322 if (err != AudioProcessing::kNoError) { 323 return err; 324 } 325 326 AllocateRenderQueue(); 327 328 return AudioProcessing::kNoError; 329} 330 331void EchoControlMobileImpl::AllocateRenderQueue() { 332 const size_t new_render_queue_element_max_size = std::max<size_t>( 333 static_cast<size_t>(1), 334 kMaxAllowedValuesOfSamplesPerFrame * num_handles_required()); 335 336 rtc::CritScope cs_render(crit_render_); 337 rtc::CritScope cs_capture(crit_capture_); 338 339 // Reallocate the queue if the queue item size is too small to fit the 340 // data to put in the queue. 341 if (render_queue_element_max_size_ < new_render_queue_element_max_size) { 342 render_queue_element_max_size_ = new_render_queue_element_max_size; 343 344 std::vector<int16_t> template_queue_element(render_queue_element_max_size_); 345 346 render_signal_queue_.reset( 347 new SwapQueue<std::vector<int16_t>, RenderQueueItemVerifier<int16_t>>( 348 kMaxNumFramesToBuffer, template_queue_element, 349 RenderQueueItemVerifier<int16_t>(render_queue_element_max_size_))); 350 351 render_queue_buffer_.resize(render_queue_element_max_size_); 352 capture_queue_buffer_.resize(render_queue_element_max_size_); 353 } else { 354 render_signal_queue_->Clear(); 355 } 356} 357 358void* EchoControlMobileImpl::CreateHandle() const { 359 return WebRtcAecm_Create(); 360} 361 362void EchoControlMobileImpl::DestroyHandle(void* handle) const { 363 // This method is only called in a non-concurrent manner during APM 364 // destruction. 365 WebRtcAecm_Free(static_cast<Handle*>(handle)); 366} 367 368int EchoControlMobileImpl::InitializeHandle(void* handle) const { 369 rtc::CritScope cs_render(crit_render_); 370 rtc::CritScope cs_capture(crit_capture_); 371 assert(handle != NULL); 372 Handle* my_handle = static_cast<Handle*>(handle); 373 if (WebRtcAecm_Init(my_handle, apm_->proc_sample_rate_hz()) != 0) { 374 return GetHandleError(my_handle); 375 } 376 if (external_echo_path_ != NULL) { 377 if (WebRtcAecm_InitEchoPath(my_handle, 378 external_echo_path_, 379 echo_path_size_bytes()) != 0) { 380 return GetHandleError(my_handle); 381 } 382 } 383 384 return AudioProcessing::kNoError; 385} 386 387int EchoControlMobileImpl::ConfigureHandle(void* handle) const { 388 rtc::CritScope cs_render(crit_render_); 389 rtc::CritScope cs_capture(crit_capture_); 390 AecmConfig config; 391 config.cngMode = comfort_noise_enabled_; 392 config.echoMode = MapSetting(routing_mode_); 393 394 return WebRtcAecm_set_config(static_cast<Handle*>(handle), config); 395} 396 397size_t EchoControlMobileImpl::num_handles_required() const { 398 // Not locked as it only relies on APM public API which is threadsafe. 399 return apm_->num_output_channels() * apm_->num_reverse_channels(); 400} 401 402int EchoControlMobileImpl::GetHandleError(void* handle) const { 403 // Not locked as it does not rely on anything in the state. 404 assert(handle != NULL); 405 return AudioProcessing::kUnspecifiedError; 406} 407} // namespace webrtc 408