Line data Source code
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 <string.h>
14 :
15 : #include "webrtc/base/constructormagic.h"
16 : #include "webrtc/modules/audio_processing/aecm/echo_control_mobile.h"
17 : #include "webrtc/modules/audio_processing/audio_buffer.h"
18 : #include "webrtc/system_wrappers/include/logging.h"
19 :
20 : namespace webrtc {
21 :
22 : namespace {
23 0 : int16_t MapSetting(EchoControlMobile::RoutingMode mode) {
24 0 : switch (mode) {
25 : case EchoControlMobile::kQuietEarpieceOrHeadset:
26 0 : return 0;
27 : case EchoControlMobile::kEarpiece:
28 0 : return 1;
29 : case EchoControlMobile::kLoudEarpiece:
30 0 : return 2;
31 : case EchoControlMobile::kSpeakerphone:
32 0 : return 3;
33 : case EchoControlMobile::kLoudSpeakerphone:
34 0 : return 4;
35 : }
36 0 : RTC_NOTREACHED();
37 0 : return -1;
38 : }
39 :
40 0 : AudioProcessing::Error MapError(int err) {
41 0 : switch (err) {
42 : case AECM_UNSUPPORTED_FUNCTION_ERROR:
43 0 : return AudioProcessing::kUnsupportedFunctionError;
44 : case AECM_NULL_POINTER_ERROR:
45 0 : return AudioProcessing::kNullPointerError;
46 : case AECM_BAD_PARAMETER_ERROR:
47 0 : return AudioProcessing::kBadParameterError;
48 : case AECM_BAD_PARAMETER_WARNING:
49 0 : return AudioProcessing::kBadStreamParameterWarning;
50 : default:
51 : // AECM_UNSPECIFIED_ERROR
52 : // AECM_UNINITIALIZED_ERROR
53 0 : return AudioProcessing::kUnspecifiedError;
54 : }
55 : }
56 : } // namespace
57 :
58 0 : size_t EchoControlMobile::echo_path_size_bytes() {
59 0 : return WebRtcAecm_echo_path_size_bytes();
60 : }
61 :
62 : struct EchoControlMobileImpl::StreamProperties {
63 : StreamProperties() = delete;
64 0 : StreamProperties(int sample_rate_hz,
65 : size_t num_reverse_channels,
66 : size_t num_output_channels)
67 0 : : sample_rate_hz(sample_rate_hz),
68 : num_reverse_channels(num_reverse_channels),
69 0 : num_output_channels(num_output_channels) {}
70 :
71 : int sample_rate_hz;
72 : size_t num_reverse_channels;
73 : size_t num_output_channels;
74 : };
75 :
76 : class EchoControlMobileImpl::Canceller {
77 : public:
78 0 : Canceller() {
79 0 : state_ = WebRtcAecm_Create();
80 0 : RTC_CHECK(state_);
81 0 : }
82 :
83 0 : ~Canceller() {
84 0 : RTC_DCHECK(state_);
85 0 : WebRtcAecm_Free(state_);
86 0 : }
87 :
88 0 : void* state() {
89 0 : RTC_DCHECK(state_);
90 0 : return state_;
91 : }
92 :
93 0 : void Initialize(int sample_rate_hz,
94 : unsigned char* external_echo_path,
95 : size_t echo_path_size_bytes) {
96 0 : RTC_DCHECK(state_);
97 0 : int error = WebRtcAecm_Init(state_, sample_rate_hz);
98 0 : RTC_DCHECK_EQ(AudioProcessing::kNoError, error);
99 0 : if (external_echo_path != NULL) {
100 0 : error = WebRtcAecm_InitEchoPath(state_, external_echo_path,
101 : echo_path_size_bytes);
102 0 : RTC_DCHECK_EQ(AudioProcessing::kNoError, error);
103 : }
104 0 : }
105 :
106 : private:
107 : void* state_;
108 : RTC_DISALLOW_COPY_AND_ASSIGN(Canceller);
109 : };
110 :
111 0 : EchoControlMobileImpl::EchoControlMobileImpl(rtc::CriticalSection* crit_render,
112 0 : rtc::CriticalSection* crit_capture)
113 : : crit_render_(crit_render),
114 : crit_capture_(crit_capture),
115 : routing_mode_(kSpeakerphone),
116 : comfort_noise_enabled_(true),
117 0 : external_echo_path_(NULL) {
118 0 : RTC_DCHECK(crit_render);
119 0 : RTC_DCHECK(crit_capture);
120 0 : }
121 :
122 0 : EchoControlMobileImpl::~EchoControlMobileImpl() {
123 0 : if (external_echo_path_ != NULL) {
124 0 : delete [] external_echo_path_;
125 0 : external_echo_path_ = NULL;
126 : }
127 0 : }
128 :
129 0 : void EchoControlMobileImpl::ProcessRenderAudio(
130 : rtc::ArrayView<const int16_t> packed_render_audio) {
131 0 : rtc::CritScope cs_capture(crit_capture_);
132 0 : if (!enabled_) {
133 0 : return;
134 : }
135 :
136 0 : RTC_DCHECK(stream_properties_);
137 :
138 0 : size_t buffer_index = 0;
139 : size_t num_frames_per_band =
140 0 : packed_render_audio.size() / (stream_properties_->num_output_channels *
141 0 : stream_properties_->num_reverse_channels);
142 :
143 0 : for (auto& canceller : cancellers_) {
144 0 : WebRtcAecm_BufferFarend(canceller->state(),
145 0 : &packed_render_audio[buffer_index],
146 0 : num_frames_per_band);
147 :
148 0 : buffer_index += num_frames_per_band;
149 : }
150 : }
151 :
152 0 : void EchoControlMobileImpl::PackRenderAudioBuffer(
153 : const AudioBuffer* audio,
154 : size_t num_output_channels,
155 : size_t num_channels,
156 : std::vector<int16_t>* packed_buffer) {
157 0 : RTC_DCHECK_GE(160, audio->num_frames_per_band());
158 0 : RTC_DCHECK_EQ(num_channels, audio->num_channels());
159 :
160 : // The ordering convention must be followed to pass to the correct AECM.
161 0 : packed_buffer->clear();
162 0 : int render_channel = 0;
163 0 : for (size_t i = 0; i < num_output_channels; i++) {
164 0 : for (size_t j = 0; j < audio->num_channels(); j++) {
165 : // Buffer the samples in the render queue.
166 : packed_buffer->insert(
167 0 : packed_buffer->end(),
168 0 : audio->split_bands_const(render_channel)[kBand0To8kHz],
169 0 : (audio->split_bands_const(render_channel)[kBand0To8kHz] +
170 0 : audio->num_frames_per_band()));
171 0 : render_channel = (render_channel + 1) % audio->num_channels();
172 : }
173 : }
174 0 : }
175 :
176 0 : size_t EchoControlMobileImpl::NumCancellersRequired(
177 : size_t num_output_channels,
178 : size_t num_reverse_channels) {
179 0 : return num_output_channels * num_reverse_channels;
180 : }
181 :
182 0 : int EchoControlMobileImpl::ProcessCaptureAudio(AudioBuffer* audio,
183 : int stream_delay_ms) {
184 0 : rtc::CritScope cs_capture(crit_capture_);
185 0 : if (!enabled_) {
186 0 : return AudioProcessing::kNoError;
187 : }
188 :
189 0 : RTC_DCHECK(stream_properties_);
190 0 : RTC_DCHECK_GE(160, audio->num_frames_per_band());
191 0 : RTC_DCHECK_EQ(audio->num_channels(), stream_properties_->num_output_channels);
192 0 : RTC_DCHECK_GE(cancellers_.size(), stream_properties_->num_reverse_channels *
193 0 : audio->num_channels());
194 :
195 0 : int err = AudioProcessing::kNoError;
196 :
197 : // The ordering convention must be followed to pass to the correct AECM.
198 0 : size_t handle_index = 0;
199 0 : for (size_t capture = 0; capture < audio->num_channels(); ++capture) {
200 : // TODO(ajm): improve how this works, possibly inside AECM.
201 : // This is kind of hacked up.
202 0 : const int16_t* noisy = audio->low_pass_reference(capture);
203 0 : const int16_t* clean = audio->split_bands_const(capture)[kBand0To8kHz];
204 0 : if (noisy == NULL) {
205 0 : noisy = clean;
206 0 : clean = NULL;
207 : }
208 0 : for (size_t render = 0; render < stream_properties_->num_reverse_channels;
209 : ++render) {
210 0 : err = WebRtcAecm_Process(cancellers_[handle_index]->state(), noisy, clean,
211 0 : audio->split_bands(capture)[kBand0To8kHz],
212 0 : audio->num_frames_per_band(), stream_delay_ms);
213 :
214 0 : if (err != AudioProcessing::kNoError) {
215 0 : return MapError(err);
216 : }
217 :
218 0 : ++handle_index;
219 : }
220 0 : for (size_t band = 1u; band < audio->num_bands(); ++band) {
221 0 : memset(audio->split_bands(capture)[band],
222 : 0,
223 0 : audio->num_frames_per_band() *
224 0 : sizeof(audio->split_bands(capture)[band][0]));
225 : }
226 : }
227 0 : return AudioProcessing::kNoError;
228 : }
229 :
230 0 : int EchoControlMobileImpl::Enable(bool enable) {
231 : // Ensure AEC and AECM are not both enabled.
232 0 : rtc::CritScope cs_render(crit_render_);
233 0 : rtc::CritScope cs_capture(crit_capture_);
234 0 : RTC_DCHECK(stream_properties_);
235 :
236 0 : if (enable &&
237 0 : stream_properties_->sample_rate_hz > AudioProcessing::kSampleRate16kHz) {
238 0 : return AudioProcessing::kBadSampleRateError;
239 : }
240 :
241 0 : if (enable && !enabled_) {
242 0 : enabled_ = enable; // Must be set before Initialize() is called.
243 :
244 : // TODO(peah): Simplify once the Enable function has been removed from
245 : // the public APM API.
246 0 : Initialize(stream_properties_->sample_rate_hz,
247 0 : stream_properties_->num_reverse_channels,
248 0 : stream_properties_->num_output_channels);
249 : } else {
250 0 : enabled_ = enable;
251 : }
252 0 : return AudioProcessing::kNoError;
253 : }
254 :
255 0 : bool EchoControlMobileImpl::is_enabled() const {
256 0 : rtc::CritScope cs(crit_capture_);
257 0 : return enabled_;
258 : }
259 :
260 0 : int EchoControlMobileImpl::set_routing_mode(RoutingMode mode) {
261 0 : if (MapSetting(mode) == -1) {
262 0 : return AudioProcessing::kBadParameterError;
263 : }
264 :
265 : {
266 0 : rtc::CritScope cs(crit_capture_);
267 0 : routing_mode_ = mode;
268 : }
269 0 : return Configure();
270 : }
271 :
272 0 : EchoControlMobile::RoutingMode EchoControlMobileImpl::routing_mode()
273 : const {
274 0 : rtc::CritScope cs(crit_capture_);
275 0 : return routing_mode_;
276 : }
277 :
278 0 : int EchoControlMobileImpl::enable_comfort_noise(bool enable) {
279 : {
280 0 : rtc::CritScope cs(crit_capture_);
281 0 : comfort_noise_enabled_ = enable;
282 : }
283 0 : return Configure();
284 : }
285 :
286 0 : bool EchoControlMobileImpl::is_comfort_noise_enabled() const {
287 0 : rtc::CritScope cs(crit_capture_);
288 0 : return comfort_noise_enabled_;
289 : }
290 :
291 0 : int EchoControlMobileImpl::SetEchoPath(const void* echo_path,
292 : size_t size_bytes) {
293 : {
294 0 : rtc::CritScope cs_render(crit_render_);
295 0 : rtc::CritScope cs_capture(crit_capture_);
296 0 : if (echo_path == NULL) {
297 0 : return AudioProcessing::kNullPointerError;
298 : }
299 0 : if (size_bytes != echo_path_size_bytes()) {
300 : // Size mismatch
301 0 : return AudioProcessing::kBadParameterError;
302 : }
303 :
304 0 : if (external_echo_path_ == NULL) {
305 0 : external_echo_path_ = new unsigned char[size_bytes];
306 : }
307 0 : memcpy(external_echo_path_, echo_path, size_bytes);
308 : }
309 :
310 : // TODO(peah): Simplify once the Enable function has been removed from
311 : // the public APM API.
312 0 : RTC_DCHECK(stream_properties_);
313 0 : Initialize(stream_properties_->sample_rate_hz,
314 0 : stream_properties_->num_reverse_channels,
315 0 : stream_properties_->num_output_channels);
316 0 : return AudioProcessing::kNoError;
317 : }
318 :
319 0 : int EchoControlMobileImpl::GetEchoPath(void* echo_path,
320 : size_t size_bytes) const {
321 0 : rtc::CritScope cs(crit_capture_);
322 0 : if (echo_path == NULL) {
323 0 : return AudioProcessing::kNullPointerError;
324 : }
325 0 : if (size_bytes != echo_path_size_bytes()) {
326 : // Size mismatch
327 0 : return AudioProcessing::kBadParameterError;
328 : }
329 0 : if (!enabled_) {
330 0 : return AudioProcessing::kNotEnabledError;
331 : }
332 :
333 : // Get the echo path from the first channel
334 : int32_t err =
335 0 : WebRtcAecm_GetEchoPath(cancellers_[0]->state(), echo_path, size_bytes);
336 0 : if (err != 0) {
337 0 : return MapError(err);
338 : }
339 :
340 0 : return AudioProcessing::kNoError;
341 : }
342 :
343 0 : void EchoControlMobileImpl::Initialize(int sample_rate_hz,
344 : size_t num_reverse_channels,
345 : size_t num_output_channels) {
346 0 : rtc::CritScope cs_render(crit_render_);
347 0 : rtc::CritScope cs_capture(crit_capture_);
348 :
349 0 : stream_properties_.reset(new StreamProperties(
350 0 : sample_rate_hz, num_reverse_channels, num_output_channels));
351 :
352 0 : if (!enabled_) {
353 0 : return;
354 : }
355 :
356 0 : if (stream_properties_->sample_rate_hz > AudioProcessing::kSampleRate16kHz) {
357 0 : LOG(LS_ERROR) << "AECM only supports 16 kHz or lower sample rates";
358 : }
359 :
360 0 : cancellers_.resize(
361 0 : NumCancellersRequired(stream_properties_->num_output_channels,
362 0 : stream_properties_->num_reverse_channels));
363 :
364 0 : for (auto& canceller : cancellers_) {
365 0 : if (!canceller) {
366 0 : canceller.reset(new Canceller());
367 : }
368 0 : canceller->Initialize(sample_rate_hz, external_echo_path_,
369 0 : echo_path_size_bytes());
370 : }
371 :
372 0 : Configure();
373 : }
374 :
375 0 : int EchoControlMobileImpl::Configure() {
376 0 : rtc::CritScope cs_render(crit_render_);
377 0 : rtc::CritScope cs_capture(crit_capture_);
378 : AecmConfig config;
379 0 : config.cngMode = comfort_noise_enabled_;
380 0 : config.echoMode = MapSetting(routing_mode_);
381 0 : int error = AudioProcessing::kNoError;
382 0 : for (auto& canceller : cancellers_) {
383 0 : int handle_error = WebRtcAecm_set_config(canceller->state(), config);
384 0 : if (handle_error != AudioProcessing::kNoError) {
385 0 : error = handle_error;
386 : }
387 : }
388 0 : return error;
389 : }
390 :
391 : } // namespace webrtc
|