blob: f7853814966783b1803a97e3764c3b577c2d7d94 [file] [log] [blame]
andrew@webrtc.orgb015cbe2012-10-22 18:19:23 +00001/*
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
andrew@webrtc.orgf1de5e92013-03-01 16:36:19 +000011#include "webrtc/modules/audio_processing/echo_control_mobile_impl.h"
andrew@webrtc.orgb015cbe2012-10-22 18:19:23 +000012
pbos@webrtc.org3f45c2e2013-08-05 16:22:53 +000013#include <assert.h>
14#include <string.h>
andrew@webrtc.orgb015cbe2012-10-22 18:19:23 +000015
andrew@webrtc.orgf1de5e92013-03-01 16:36:19 +000016#include "webrtc/modules/audio_processing/aecm/include/echo_control_mobile.h"
andrew@webrtc.orgf1de5e92013-03-01 16:36:19 +000017#include "webrtc/modules/audio_processing/audio_buffer.h"
pbos@webrtc.org9fb16132013-05-28 08:11:59 +000018#include "webrtc/modules/audio_processing/audio_processing_impl.h"
andrew@webrtc.orgf1de5e92013-03-01 16:36:19 +000019#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
20#include "webrtc/system_wrappers/interface/logging.h"
andrew@webrtc.orgb015cbe2012-10-22 18:19:23 +000021
22namespace webrtc {
23
24typedef void Handle;
25
26namespace {
pbos@webrtc.org3f6d5e02013-04-10 07:50:54 +000027int16_t MapSetting(EchoControlMobile::RoutingMode mode) {
andrew@webrtc.orgb015cbe2012-10-22 18:19:23 +000028 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 }
40 assert(false);
41 return -1;
42}
43
44AudioProcessing::Error 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
62size_t EchoControlMobile::echo_path_size_bytes() {
63 return WebRtcAecm_echo_path_size_bytes();
64}
65
66EchoControlMobileImpl::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
73EchoControlMobileImpl::~EchoControlMobileImpl() {
74 if (external_echo_path_ != NULL) {
75 delete [] external_echo_path_;
76 external_echo_path_ = NULL;
77 }
78}
79
80int 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),
pbos@webrtc.org3f6d5e02013-04-10 07:50:54 +000098 static_cast<int16_t>(audio->samples_per_split_channel()));
andrew@webrtc.orgb015cbe2012-10-22 18:19:23 +000099
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
111int 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.
pbos@webrtc.org3f6d5e02013-04-10 07:50:54 +0000130 int16_t* noisy = audio->low_pass_reference(i);
131 int16_t* clean = audio->low_pass_split_data(i);
andrew@webrtc.orgb015cbe2012-10-22 18:19:23 +0000132 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),
pbos@webrtc.org3f6d5e02013-04-10 07:50:54 +0000143 static_cast<int16_t>(audio->samples_per_split_channel()),
andrew@webrtc.orgb015cbe2012-10-22 18:19:23 +0000144 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
157int 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
167bool EchoControlMobileImpl::is_enabled() const {
168 return is_component_enabled();
169}
170
171int 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
181EchoControlMobile::RoutingMode EchoControlMobileImpl::routing_mode()
182 const {
183 return routing_mode_;
184}
185
186int EchoControlMobileImpl::enable_comfort_noise(bool enable) {
187 CriticalSectionScoped crit_scoped(apm_->crit());
188 comfort_noise_enabled_ = enable;
189 return Configure();
190}
191
192bool EchoControlMobileImpl::is_comfort_noise_enabled() const {
193 return comfort_noise_enabled_;
194}
195
196int 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
215int 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
238int EchoControlMobileImpl::Initialize() {
239 if (!is_component_enabled()) {
240 return apm_->kNoError;
241 }
242
243 if (apm_->sample_rate_hz() == apm_->kSampleRate32kHz) {
andrew@webrtc.orgf1de5e92013-03-01 16:36:19 +0000244 LOG(LS_ERROR) << "AECM only supports 16 kHz or lower sample rates";
andrew@webrtc.orgb015cbe2012-10-22 18:19:23 +0000245 return apm_->kBadSampleRateError;
246 }
247
248 return ProcessingComponent::Initialize();
249}
250
251void* EchoControlMobileImpl::CreateHandle() const {
252 Handle* handle = NULL;
253 if (WebRtcAecm_Create(&handle) != apm_->kNoError) {
254 handle = NULL;
255 } else {
256 assert(handle != NULL);
257 }
258
259 return handle;
260}
261
262int EchoControlMobileImpl::DestroyHandle(void* handle) const {
263 return WebRtcAecm_Free(static_cast<Handle*>(handle));
264}
265
266int EchoControlMobileImpl::InitializeHandle(void* handle) const {
267 assert(handle != NULL);
268 Handle* my_handle = static_cast<Handle*>(handle);
269 if (WebRtcAecm_Init(my_handle, apm_->sample_rate_hz()) != 0) {
270 return GetHandleError(my_handle);
271 }
272 if (external_echo_path_ != NULL) {
273 if (WebRtcAecm_InitEchoPath(my_handle,
274 external_echo_path_,
275 echo_path_size_bytes()) != 0) {
276 return GetHandleError(my_handle);
277 }
278 }
279
280 return apm_->kNoError;
281}
282
283int EchoControlMobileImpl::ConfigureHandle(void* handle) const {
284 AecmConfig config;
285 config.cngMode = comfort_noise_enabled_;
286 config.echoMode = MapSetting(routing_mode_);
287
288 return WebRtcAecm_set_config(static_cast<Handle*>(handle), config);
289}
290
291int EchoControlMobileImpl::num_handles_required() const {
292 return apm_->num_output_channels() *
293 apm_->num_reverse_channels();
294}
295
296int EchoControlMobileImpl::GetHandleError(void* handle) const {
297 assert(handle != NULL);
298 return MapError(WebRtcAecm_get_error_code(static_cast<Handle*>(handle)));
299}
300} // namespace webrtc