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