blob: ad3c731e73b54c15cd8e72531b83edfd1cc199e0 [file] [log] [blame]
niklase@google.com470e71d2011-07-07 08:21:25 +00001/*
bjornv@webrtc.org0c6f9312012-01-30 09:39:08 +00002 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
niklase@google.com470e71d2011-07-07 08:21:25 +00003 *
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
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020011#include "modules/audio_processing/echo_control_mobile_impl.h"
niklase@google.com470e71d2011-07-07 08:21:25 +000012
pbos@webrtc.org12dc1a32013-08-05 16:22:53 +000013#include <string.h>
niklase@google.com470e71d2011-07-07 08:21:25 +000014
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020015#include "modules/audio_processing/aecm/echo_control_mobile.h"
16#include "modules/audio_processing/audio_buffer.h"
17#include "rtc_base/constructormagic.h"
18#include "rtc_base/logging.h"
niklase@google.com470e71d2011-07-07 08:21:25 +000019
20namespace webrtc {
21
niklase@google.com470e71d2011-07-07 08:21:25 +000022namespace {
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +000023int16_t MapSetting(EchoControlMobile::RoutingMode mode) {
niklase@google.com470e71d2011-07-07 08:21:25 +000024 switch (mode) {
25 case EchoControlMobile::kQuietEarpieceOrHeadset:
26 return 0;
27 case EchoControlMobile::kEarpiece:
28 return 1;
29 case EchoControlMobile::kLoudEarpiece:
30 return 2;
31 case EchoControlMobile::kSpeakerphone:
32 return 3;
33 case EchoControlMobile::kLoudSpeakerphone:
34 return 4;
niklase@google.com470e71d2011-07-07 08:21:25 +000035 }
nisseeb4ca4e2017-01-12 02:24:27 -080036 RTC_NOTREACHED();
mflodman@webrtc.org657b2a42012-02-06 11:06:01 +000037 return -1;
niklase@google.com470e71d2011-07-07 08:21:25 +000038}
39
peahfa6228e2015-11-16 16:27:42 -080040AudioProcessing::Error MapError(int err) {
41 switch (err) {
42 case AECM_UNSUPPORTED_FUNCTION_ERROR:
43 return AudioProcessing::kUnsupportedFunctionError;
44 case AECM_NULL_POINTER_ERROR:
45 return AudioProcessing::kNullPointerError;
46 case AECM_BAD_PARAMETER_ERROR:
47 return AudioProcessing::kBadParameterError;
48 case AECM_BAD_PARAMETER_WARNING:
49 return AudioProcessing::kBadStreamParameterWarning;
50 default:
51 // AECM_UNSPECIFIED_ERROR
52 // AECM_UNINITIALIZED_ERROR
53 return AudioProcessing::kUnspecifiedError;
54 }
55}
niklase@google.com470e71d2011-07-07 08:21:25 +000056} // namespace
57
ajm@google.com22e65152011-07-18 18:03:01 +000058size_t EchoControlMobile::echo_path_size_bytes() {
peahbb9edbd2016-03-10 12:54:25 -080059 return WebRtcAecm_echo_path_size_bytes();
ajm@google.com22e65152011-07-18 18:03:01 +000060}
61
peah253534d2016-03-15 04:32:28 -070062struct EchoControlMobileImpl::StreamProperties {
63 StreamProperties() = delete;
64 StreamProperties(int sample_rate_hz,
65 size_t num_reverse_channels,
66 size_t num_output_channels)
67 : sample_rate_hz(sample_rate_hz),
68 num_reverse_channels(num_reverse_channels),
69 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
peahbb9edbd2016-03-10 12:54:25 -080076class EchoControlMobileImpl::Canceller {
77 public:
78 Canceller() {
79 state_ = WebRtcAecm_Create();
80 RTC_CHECK(state_);
81 }
82
83 ~Canceller() {
84 RTC_DCHECK(state_);
85 WebRtcAecm_Free(state_);
86 }
87
88 void* state() {
89 RTC_DCHECK(state_);
90 return state_;
91 }
92
93 void Initialize(int sample_rate_hz,
94 unsigned char* external_echo_path,
95 size_t echo_path_size_bytes) {
96 RTC_DCHECK(state_);
97 int error = WebRtcAecm_Init(state_, sample_rate_hz);
98 RTC_DCHECK_EQ(AudioProcessing::kNoError, error);
99 if (external_echo_path != NULL) {
100 error = WebRtcAecm_InitEchoPath(state_, external_echo_path,
101 echo_path_size_bytes);
102 RTC_DCHECK_EQ(AudioProcessing::kNoError, error);
103 }
104 }
105
106 private:
107 void* state_;
108 RTC_DISALLOW_COPY_AND_ASSIGN(Canceller);
109};
110
peah253534d2016-03-15 04:32:28 -0700111EchoControlMobileImpl::EchoControlMobileImpl(rtc::CriticalSection* crit_render,
peahdf3efa82015-11-28 12:35:15 -0800112 rtc::CriticalSection* crit_capture)
peah253534d2016-03-15 04:32:28 -0700113 : crit_render_(crit_render),
peahdf3efa82015-11-28 12:35:15 -0800114 crit_capture_(crit_capture),
peahfa6228e2015-11-16 16:27:42 -0800115 routing_mode_(kSpeakerphone),
116 comfort_noise_enabled_(true),
peaha0624602016-10-25 04:45:24 -0700117 external_echo_path_(NULL) {
peahdf3efa82015-11-28 12:35:15 -0800118 RTC_DCHECK(crit_render);
119 RTC_DCHECK(crit_capture);
120}
niklase@google.com470e71d2011-07-07 08:21:25 +0000121
bjornv@google.comc4b939c2011-07-13 08:09:56 +0000122EchoControlMobileImpl::~EchoControlMobileImpl() {
123 if (external_echo_path_ != NULL) {
124 delete [] external_echo_path_;
125 external_echo_path_ = NULL;
126 }
127}
niklase@google.com470e71d2011-07-07 08:21:25 +0000128
peaha0624602016-10-25 04:45:24 -0700129void EchoControlMobileImpl::ProcessRenderAudio(
130 rtc::ArrayView<const int16_t> packed_render_audio) {
peahdf3efa82015-11-28 12:35:15 -0800131 rtc::CritScope cs_capture(crit_capture_);
peahbb9edbd2016-03-10 12:54:25 -0800132 if (!enabled_) {
peahfa6228e2015-11-16 16:27:42 -0800133 return;
134 }
135
peaha0624602016-10-25 04:45:24 -0700136 RTC_DCHECK(stream_properties_);
peahfa6228e2015-11-16 16:27:42 -0800137
peaha0624602016-10-25 04:45:24 -0700138 size_t buffer_index = 0;
139 size_t num_frames_per_band =
140 packed_render_audio.size() / (stream_properties_->num_output_channels *
141 stream_properties_->num_reverse_channels);
peahbb9edbd2016-03-10 12:54:25 -0800142
peaha0624602016-10-25 04:45:24 -0700143 for (auto& canceller : cancellers_) {
144 WebRtcAecm_BufferFarend(canceller->state(),
145 &packed_render_audio[buffer_index],
146 num_frames_per_band);
147
148 buffer_index += num_frames_per_band;
149 }
150}
151
152void EchoControlMobileImpl::PackRenderAudioBuffer(
153 const AudioBuffer* audio,
154 size_t num_output_channels,
155 size_t num_channels,
156 std::vector<int16_t>* packed_buffer) {
kwibergaf476c72016-11-28 15:21:39 -0800157 RTC_DCHECK_GE(160, audio->num_frames_per_band());
peaha0624602016-10-25 04:45:24 -0700158 RTC_DCHECK_EQ(num_channels, audio->num_channels());
159
160 // The ordering convention must be followed to pass to the correct AECM.
161 packed_buffer->clear();
162 int render_channel = 0;
163 for (size_t i = 0; i < num_output_channels; i++) {
164 for (size_t j = 0; j < audio->num_channels(); j++) {
165 // Buffer the samples in the render queue.
166 packed_buffer->insert(
167 packed_buffer->end(),
168 audio->split_bands_const(render_channel)[kBand0To8kHz],
169 (audio->split_bands_const(render_channel)[kBand0To8kHz] +
170 audio->num_frames_per_band()));
171 render_channel = (render_channel + 1) % audio->num_channels();
peahfa6228e2015-11-16 16:27:42 -0800172 }
173 }
174}
175
peaha0624602016-10-25 04:45:24 -0700176size_t EchoControlMobileImpl::NumCancellersRequired(
177 size_t num_output_channels,
178 size_t num_reverse_channels) {
179 return num_output_channels * num_reverse_channels;
180}
181
peah253534d2016-03-15 04:32:28 -0700182int EchoControlMobileImpl::ProcessCaptureAudio(AudioBuffer* audio,
183 int stream_delay_ms) {
peahdf3efa82015-11-28 12:35:15 -0800184 rtc::CritScope cs_capture(crit_capture_);
peahbb9edbd2016-03-10 12:54:25 -0800185 if (!enabled_) {
peahdf3efa82015-11-28 12:35:15 -0800186 return AudioProcessing::kNoError;
niklase@google.com470e71d2011-07-07 08:21:25 +0000187 }
188
peah253534d2016-03-15 04:32:28 -0700189 RTC_DCHECK(stream_properties_);
kwibergaf476c72016-11-28 15:21:39 -0800190 RTC_DCHECK_GE(160, audio->num_frames_per_band());
peah253534d2016-03-15 04:32:28 -0700191 RTC_DCHECK_EQ(audio->num_channels(), stream_properties_->num_output_channels);
192 RTC_DCHECK_GE(cancellers_.size(), stream_properties_->num_reverse_channels *
193 audio->num_channels());
niklase@google.com470e71d2011-07-07 08:21:25 +0000194
peahdf3efa82015-11-28 12:35:15 -0800195 int err = AudioProcessing::kNoError;
niklase@google.com470e71d2011-07-07 08:21:25 +0000196
197 // The ordering convention must be followed to pass to the correct AECM.
198 size_t handle_index = 0;
peahbb9edbd2016-03-10 12:54:25 -0800199 for (size_t capture = 0; capture < audio->num_channels(); ++capture) {
niklase@google.com470e71d2011-07-07 08:21:25 +0000200 // TODO(ajm): improve how this works, possibly inside AECM.
201 // This is kind of hacked up.
peahbb9edbd2016-03-10 12:54:25 -0800202 const int16_t* noisy = audio->low_pass_reference(capture);
203 const int16_t* clean = audio->split_bands_const(capture)[kBand0To8kHz];
niklase@google.com470e71d2011-07-07 08:21:25 +0000204 if (noisy == NULL) {
205 noisy = clean;
206 clean = NULL;
207 }
peah253534d2016-03-15 04:32:28 -0700208 for (size_t render = 0; render < stream_properties_->num_reverse_channels;
209 ++render) {
peahbb9edbd2016-03-10 12:54:25 -0800210 err = WebRtcAecm_Process(cancellers_[handle_index]->state(), noisy, clean,
211 audio->split_bands(capture)[kBand0To8kHz],
peah253534d2016-03-15 04:32:28 -0700212 audio->num_frames_per_band(), stream_delay_ms);
niklase@google.com470e71d2011-07-07 08:21:25 +0000213
peahbb9edbd2016-03-10 12:54:25 -0800214 if (err != AudioProcessing::kNoError) {
peahfa6228e2015-11-16 16:27:42 -0800215 return MapError(err);
peahbb9edbd2016-03-10 12:54:25 -0800216 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000217
peahbb9edbd2016-03-10 12:54:25 -0800218 ++handle_index;
niklase@google.com470e71d2011-07-07 08:21:25 +0000219 }
aluebs776593b2016-03-15 14:04:58 -0700220 for (size_t band = 1u; band < audio->num_bands(); ++band) {
221 memset(audio->split_bands(capture)[band],
222 0,
223 audio->num_frames_per_band() *
224 sizeof(audio->split_bands(capture)[band][0]));
225 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000226 }
peahdf3efa82015-11-28 12:35:15 -0800227 return AudioProcessing::kNoError;
niklase@google.com470e71d2011-07-07 08:21:25 +0000228}
229
230int EchoControlMobileImpl::Enable(bool enable) {
niklase@google.com470e71d2011-07-07 08:21:25 +0000231 // Ensure AEC and AECM are not both enabled.
peahdf3efa82015-11-28 12:35:15 -0800232 rtc::CritScope cs_render(crit_render_);
233 rtc::CritScope cs_capture(crit_capture_);
peah253534d2016-03-15 04:32:28 -0700234 RTC_DCHECK(stream_properties_);
niklase@google.com470e71d2011-07-07 08:21:25 +0000235
peahbb9edbd2016-03-10 12:54:25 -0800236 if (enable &&
peah253534d2016-03-15 04:32:28 -0700237 stream_properties_->sample_rate_hz > AudioProcessing::kSampleRate16kHz) {
peahbb9edbd2016-03-10 12:54:25 -0800238 return AudioProcessing::kBadSampleRateError;
239 }
240
241 if (enable && !enabled_) {
242 enabled_ = enable; // Must be set before Initialize() is called.
peah253534d2016-03-15 04:32:28 -0700243
244 // TODO(peah): Simplify once the Enable function has been removed from
245 // the public APM API.
246 Initialize(stream_properties_->sample_rate_hz,
247 stream_properties_->num_reverse_channels,
248 stream_properties_->num_output_channels);
peahbb9edbd2016-03-10 12:54:25 -0800249 } else {
250 enabled_ = enable;
251 }
252 return AudioProcessing::kNoError;
niklase@google.com470e71d2011-07-07 08:21:25 +0000253}
254
255bool EchoControlMobileImpl::is_enabled() const {
peahdf3efa82015-11-28 12:35:15 -0800256 rtc::CritScope cs(crit_capture_);
peahbb9edbd2016-03-10 12:54:25 -0800257 return enabled_;
niklase@google.com470e71d2011-07-07 08:21:25 +0000258}
259
260int EchoControlMobileImpl::set_routing_mode(RoutingMode mode) {
niklase@google.com470e71d2011-07-07 08:21:25 +0000261 if (MapSetting(mode) == -1) {
peahdf3efa82015-11-28 12:35:15 -0800262 return AudioProcessing::kBadParameterError;
niklase@google.com470e71d2011-07-07 08:21:25 +0000263 }
264
peahdf3efa82015-11-28 12:35:15 -0800265 {
266 rtc::CritScope cs(crit_capture_);
267 routing_mode_ = mode;
268 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000269 return Configure();
270}
271
272EchoControlMobile::RoutingMode EchoControlMobileImpl::routing_mode()
273 const {
peahdf3efa82015-11-28 12:35:15 -0800274 rtc::CritScope cs(crit_capture_);
niklase@google.com470e71d2011-07-07 08:21:25 +0000275 return routing_mode_;
276}
277
278int EchoControlMobileImpl::enable_comfort_noise(bool enable) {
peahdf3efa82015-11-28 12:35:15 -0800279 {
280 rtc::CritScope cs(crit_capture_);
281 comfort_noise_enabled_ = enable;
282 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000283 return Configure();
284}
285
286bool EchoControlMobileImpl::is_comfort_noise_enabled() const {
peahdf3efa82015-11-28 12:35:15 -0800287 rtc::CritScope cs(crit_capture_);
niklase@google.com470e71d2011-07-07 08:21:25 +0000288 return comfort_noise_enabled_;
289}
290
bjornv@google.comc4b939c2011-07-13 08:09:56 +0000291int EchoControlMobileImpl::SetEchoPath(const void* echo_path,
ajm@google.com22e65152011-07-18 18:03:01 +0000292 size_t size_bytes) {
peahdf3efa82015-11-28 12:35:15 -0800293 {
294 rtc::CritScope cs_render(crit_render_);
295 rtc::CritScope cs_capture(crit_capture_);
296 if (echo_path == NULL) {
297 return AudioProcessing::kNullPointerError;
298 }
299 if (size_bytes != echo_path_size_bytes()) {
300 // Size mismatch
301 return AudioProcessing::kBadParameterError;
302 }
bjornv@google.comc4b939c2011-07-13 08:09:56 +0000303
peahdf3efa82015-11-28 12:35:15 -0800304 if (external_echo_path_ == NULL) {
305 external_echo_path_ = new unsigned char[size_bytes];
306 }
307 memcpy(external_echo_path_, echo_path, size_bytes);
bjornv@google.comc4b939c2011-07-13 08:09:56 +0000308 }
bjornv@google.comc4b939c2011-07-13 08:09:56 +0000309
peah253534d2016-03-15 04:32:28 -0700310 // TODO(peah): Simplify once the Enable function has been removed from
311 // the public APM API.
312 RTC_DCHECK(stream_properties_);
313 Initialize(stream_properties_->sample_rate_hz,
314 stream_properties_->num_reverse_channels,
315 stream_properties_->num_output_channels);
peahbb9edbd2016-03-10 12:54:25 -0800316 return AudioProcessing::kNoError;
bjornv@google.comc4b939c2011-07-13 08:09:56 +0000317}
318
319int EchoControlMobileImpl::GetEchoPath(void* echo_path,
ajm@google.com22e65152011-07-18 18:03:01 +0000320 size_t size_bytes) const {
peahdf3efa82015-11-28 12:35:15 -0800321 rtc::CritScope cs(crit_capture_);
bjornv@google.comc4b939c2011-07-13 08:09:56 +0000322 if (echo_path == NULL) {
peahdf3efa82015-11-28 12:35:15 -0800323 return AudioProcessing::kNullPointerError;
bjornv@google.comc4b939c2011-07-13 08:09:56 +0000324 }
ajm@google.com22e65152011-07-18 18:03:01 +0000325 if (size_bytes != echo_path_size_bytes()) {
bjornv@google.comc4b939c2011-07-13 08:09:56 +0000326 // Size mismatch
peahdf3efa82015-11-28 12:35:15 -0800327 return AudioProcessing::kBadParameterError;
bjornv@google.comc4b939c2011-07-13 08:09:56 +0000328 }
peahbb9edbd2016-03-10 12:54:25 -0800329 if (!enabled_) {
peahdf3efa82015-11-28 12:35:15 -0800330 return AudioProcessing::kNotEnabledError;
bjornv@google.comc4b939c2011-07-13 08:09:56 +0000331 }
332
333 // Get the echo path from the first channel
peahbb9edbd2016-03-10 12:54:25 -0800334 int32_t err =
335 WebRtcAecm_GetEchoPath(cancellers_[0]->state(), echo_path, size_bytes);
336 if (err != 0) {
peahfa6228e2015-11-16 16:27:42 -0800337 return MapError(err);
peahbb9edbd2016-03-10 12:54:25 -0800338 }
bjornv@google.comc4b939c2011-07-13 08:09:56 +0000339
peahdf3efa82015-11-28 12:35:15 -0800340 return AudioProcessing::kNoError;
bjornv@google.comc4b939c2011-07-13 08:09:56 +0000341}
342
peah253534d2016-03-15 04:32:28 -0700343void EchoControlMobileImpl::Initialize(int sample_rate_hz,
344 size_t num_reverse_channels,
345 size_t num_output_channels) {
peahbb9edbd2016-03-10 12:54:25 -0800346 rtc::CritScope cs_render(crit_render_);
347 rtc::CritScope cs_capture(crit_capture_);
peah253534d2016-03-15 04:32:28 -0700348
349 stream_properties_.reset(new StreamProperties(
350 sample_rate_hz, num_reverse_channels, num_output_channels));
351
peahbb9edbd2016-03-10 12:54:25 -0800352 if (!enabled_) {
353 return;
niklase@google.com470e71d2011-07-07 08:21:25 +0000354 }
355
peah253534d2016-03-15 04:32:28 -0700356 if (stream_properties_->sample_rate_hz > AudioProcessing::kSampleRate16kHz) {
perkjdfc28702016-03-09 16:23:23 -0800357 LOG(LS_ERROR) << "AECM only supports 16 kHz or lower sample rates";
niklase@google.com470e71d2011-07-07 08:21:25 +0000358 }
359
peaha0624602016-10-25 04:45:24 -0700360 cancellers_.resize(
361 NumCancellersRequired(stream_properties_->num_output_channels,
362 stream_properties_->num_reverse_channels));
363
peahbb9edbd2016-03-10 12:54:25 -0800364 for (auto& canceller : cancellers_) {
365 if (!canceller) {
366 canceller.reset(new Canceller());
367 }
368 canceller->Initialize(sample_rate_hz, external_echo_path_,
369 echo_path_size_bytes());
peahfa6228e2015-11-16 16:27:42 -0800370 }
371
peahbb9edbd2016-03-10 12:54:25 -0800372 Configure();
niklase@google.com470e71d2011-07-07 08:21:25 +0000373}
374
peahbb9edbd2016-03-10 12:54:25 -0800375int EchoControlMobileImpl::Configure() {
peahdf3efa82015-11-28 12:35:15 -0800376 rtc::CritScope cs_render(crit_render_);
377 rtc::CritScope cs_capture(crit_capture_);
niklase@google.com470e71d2011-07-07 08:21:25 +0000378 AecmConfig config;
379 config.cngMode = comfort_noise_enabled_;
380 config.echoMode = MapSetting(routing_mode_);
peahbb9edbd2016-03-10 12:54:25 -0800381 int error = AudioProcessing::kNoError;
382 for (auto& canceller : cancellers_) {
383 int handle_error = WebRtcAecm_set_config(canceller->state(), config);
384 if (handle_error != AudioProcessing::kNoError) {
385 error = handle_error;
386 }
387 }
388 return error;
niklase@google.com470e71d2011-07-07 08:21:25 +0000389}
390
niklase@google.com470e71d2011-07-07 08:21:25 +0000391} // namespace webrtc