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
15 #include "critical_section_wrapper.h"
16 #include "echo_control_mobile.h"
17
18 #include "audio_processing_impl.h"
19 #include "audio_buffer.h"
20
21 namespace webrtc {
22
23 typedef void Handle;
24
25 namespace {
MapSetting(EchoControlMobile::RoutingMode mode)26 WebRtc_Word16 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 default:
39 return -1;
40 }
41 }
42
MapError(int err)43 int MapError(int err) {
44 switch (err) {
45 case AECM_UNSUPPORTED_FUNCTION_ERROR:
46 return AudioProcessing::kUnsupportedFunctionError;
47 case AECM_BAD_PARAMETER_ERROR:
48 return AudioProcessing::kBadParameterError;
49 case AECM_BAD_PARAMETER_WARNING:
50 return AudioProcessing::kBadStreamParameterWarning;
51 default:
52 // AECMOBFIX_UNSPECIFIED_ERROR
53 // AECMOBFIX_UNINITIALIZED_ERROR
54 // AECMOBFIX_NULL_POINTER_ERROR
55 return AudioProcessing::kUnspecifiedError;
56 }
57 }
58 } // namespace
59
EchoControlMobileImpl(const AudioProcessingImpl * apm)60 EchoControlMobileImpl::EchoControlMobileImpl(const AudioProcessingImpl* apm)
61 : ProcessingComponent(apm),
62 apm_(apm),
63 routing_mode_(kSpeakerphone),
64 comfort_noise_enabled_(true) {}
65
~EchoControlMobileImpl()66 EchoControlMobileImpl::~EchoControlMobileImpl() {}
67
ProcessRenderAudio(const AudioBuffer * audio)68 int EchoControlMobileImpl::ProcessRenderAudio(const AudioBuffer* audio) {
69 if (!is_component_enabled()) {
70 return apm_->kNoError;
71 }
72
73 assert(audio->samples_per_split_channel() <= 160);
74 assert(audio->num_channels() == apm_->num_reverse_channels());
75
76 int err = apm_->kNoError;
77
78 // The ordering convention must be followed to pass to the correct AECM.
79 size_t handle_index = 0;
80 for (int i = 0; i < apm_->num_output_channels(); i++) {
81 for (int j = 0; j < audio->num_channels(); j++) {
82 Handle* my_handle = static_cast<Handle*>(handle(handle_index));
83 err = WebRtcAecm_BufferFarend(
84 my_handle,
85 audio->low_pass_split_data(j),
86 static_cast<WebRtc_Word16>(audio->samples_per_split_channel()));
87
88 if (err != apm_->kNoError) {
89 return GetHandleError(my_handle); // TODO(ajm): warning possible?
90 }
91
92 handle_index++;
93 }
94 }
95
96 return apm_->kNoError;
97 }
98
ProcessCaptureAudio(AudioBuffer * audio)99 int EchoControlMobileImpl::ProcessCaptureAudio(AudioBuffer* audio) {
100 if (!is_component_enabled()) {
101 return apm_->kNoError;
102 }
103
104 if (!apm_->was_stream_delay_set()) {
105 return apm_->kStreamParameterNotSetError;
106 }
107
108 assert(audio->samples_per_split_channel() <= 160);
109 assert(audio->num_channels() == apm_->num_output_channels());
110
111 int err = apm_->kNoError;
112
113 // The ordering convention must be followed to pass to the correct AECM.
114 size_t handle_index = 0;
115 for (int i = 0; i < audio->num_channels(); i++) {
116 // TODO(ajm): improve how this works, possibly inside AECM.
117 // This is kind of hacked up.
118 WebRtc_Word16* noisy = audio->low_pass_reference(i);
119 WebRtc_Word16* clean = audio->low_pass_split_data(i);
120 if (noisy == NULL) {
121 noisy = clean;
122 clean = NULL;
123 }
124 for (int j = 0; j < apm_->num_reverse_channels(); j++) {
125 Handle* my_handle = static_cast<Handle*>(handle(handle_index));
126 err = WebRtcAecm_Process(
127 my_handle,
128 noisy,
129 clean,
130 audio->low_pass_split_data(i),
131 static_cast<WebRtc_Word16>(audio->samples_per_split_channel()),
132 apm_->stream_delay_ms());
133
134 if (err != apm_->kNoError) {
135 return GetHandleError(my_handle); // TODO(ajm): warning possible?
136 }
137
138 handle_index++;
139 }
140 }
141
142 return apm_->kNoError;
143 }
144
Enable(bool enable)145 int EchoControlMobileImpl::Enable(bool enable) {
146 CriticalSectionScoped crit_scoped(*apm_->crit());
147 // Ensure AEC and AECM are not both enabled.
148 if (enable && apm_->echo_cancellation()->is_enabled()) {
149 return apm_->kBadParameterError;
150 }
151
152 return EnableComponent(enable);
153 }
154
is_enabled() const155 bool EchoControlMobileImpl::is_enabled() const {
156 return is_component_enabled();
157 }
158
set_routing_mode(RoutingMode mode)159 int EchoControlMobileImpl::set_routing_mode(RoutingMode mode) {
160 CriticalSectionScoped crit_scoped(*apm_->crit());
161 if (MapSetting(mode) == -1) {
162 return apm_->kBadParameterError;
163 }
164
165 routing_mode_ = mode;
166 return Configure();
167 }
168
routing_mode() const169 EchoControlMobile::RoutingMode EchoControlMobileImpl::routing_mode()
170 const {
171 return routing_mode_;
172 }
173
enable_comfort_noise(bool enable)174 int EchoControlMobileImpl::enable_comfort_noise(bool enable) {
175 CriticalSectionScoped crit_scoped(*apm_->crit());
176 comfort_noise_enabled_ = enable;
177 return Configure();
178 }
179
is_comfort_noise_enabled() const180 bool EchoControlMobileImpl::is_comfort_noise_enabled() const {
181 return comfort_noise_enabled_;
182 }
183
Initialize()184 int EchoControlMobileImpl::Initialize() {
185 if (!is_component_enabled()) {
186 return apm_->kNoError;
187 }
188
189 if (apm_->sample_rate_hz() == apm_->kSampleRate32kHz) {
190 // AECM doesn't support super-wideband.
191 return apm_->kBadSampleRateError;
192 }
193
194 return ProcessingComponent::Initialize();
195 }
196
get_version(char * version,int version_len_bytes) const197 int EchoControlMobileImpl::get_version(char* version,
198 int version_len_bytes) const {
199 if (WebRtcAecm_get_version(version, version_len_bytes) != 0) {
200 return apm_->kBadParameterError;
201 }
202
203 return apm_->kNoError;
204 }
205
CreateHandle() const206 void* EchoControlMobileImpl::CreateHandle() const {
207 Handle* handle = NULL;
208 if (WebRtcAecm_Create(&handle) != apm_->kNoError) {
209 handle = NULL;
210 } else {
211 assert(handle != NULL);
212 }
213
214 return handle;
215 }
216
DestroyHandle(void * handle) const217 int EchoControlMobileImpl::DestroyHandle(void* handle) const {
218 return WebRtcAecm_Free(static_cast<Handle*>(handle));
219 }
220
InitializeHandle(void * handle) const221 int EchoControlMobileImpl::InitializeHandle(void* handle) const {
222 return WebRtcAecm_Init(static_cast<Handle*>(handle),
223 apm_->sample_rate_hz(),
224 48000); // Dummy value. This isn't actually
225 // required by AECM.
226 }
227
ConfigureHandle(void * handle) const228 int EchoControlMobileImpl::ConfigureHandle(void* handle) const {
229 AecmConfig config;
230 config.cngMode = comfort_noise_enabled_;
231 config.echoMode = MapSetting(routing_mode_);
232
233 return WebRtcAecm_set_config(static_cast<Handle*>(handle), config);
234 }
235
num_handles_required() const236 int EchoControlMobileImpl::num_handles_required() const {
237 return apm_->num_output_channels() *
238 apm_->num_reverse_channels();
239 }
240
GetHandleError(void * handle) const241 int EchoControlMobileImpl::GetHandleError(void* handle) const {
242 assert(handle != NULL);
243 return MapError(WebRtcAecm_get_error_code(static_cast<Handle*>(handle)));
244 }
245 } // namespace webrtc
246