Home | History | Annotate | Download | only in audio_processing
      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