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 {
MapSetting(EchoControlMobile::RoutingMode mode)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 
MapError(int err)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 
echo_path_size_bytes()62 size_t EchoControlMobile::echo_path_size_bytes() {
63     return WebRtcAecm_echo_path_size_bytes();
64 }
65 
EchoControlMobileImpl(const AudioProcessingImpl * apm)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 
~EchoControlMobileImpl()73 EchoControlMobileImpl::~EchoControlMobileImpl() {
74     if (external_echo_path_ != NULL) {
75       delete [] external_echo_path_;
76       external_echo_path_ = NULL;
77     }
78 }
79 
ProcessRenderAudio(const AudioBuffer * audio)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 
ProcessCaptureAudio(AudioBuffer * audio)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 
Enable(bool enable)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 
is_enabled() const167 bool EchoControlMobileImpl::is_enabled() const {
168   return is_component_enabled();
169 }
170 
set_routing_mode(RoutingMode mode)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 
routing_mode() const181 EchoControlMobile::RoutingMode EchoControlMobileImpl::routing_mode()
182     const {
183   return routing_mode_;
184 }
185 
enable_comfort_noise(bool enable)186 int EchoControlMobileImpl::enable_comfort_noise(bool enable) {
187   CriticalSectionScoped crit_scoped(*apm_->crit());
188   comfort_noise_enabled_ = enable;
189   return Configure();
190 }
191 
is_comfort_noise_enabled() const192 bool EchoControlMobileImpl::is_comfort_noise_enabled() const {
193   return comfort_noise_enabled_;
194 }
195 
SetEchoPath(const void * echo_path,size_t size_bytes)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 
GetEchoPath(void * echo_path,size_t size_bytes) const215 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 
Initialize()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 
get_version(char * version,int version_len_bytes) const251 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 
CreateHandle() const260 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 
DestroyHandle(void * handle) const271 int EchoControlMobileImpl::DestroyHandle(void* handle) const {
272   return WebRtcAecm_Free(static_cast<Handle*>(handle));
273 }
274 
InitializeHandle(void * handle) const275 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 
ConfigureHandle(void * handle) const292 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 
num_handles_required() const300 int EchoControlMobileImpl::num_handles_required() const {
301   return apm_->num_output_channels() *
302          apm_->num_reverse_channels();
303 }
304 
GetHandleError(void * handle) const305 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