blob: 272da7a71ed62076ae6164ecb2f24735e79cc27f [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),
Sam Zackrissone507b0c2018-07-20 15:22:50 +0200116 comfort_noise_enabled_(false),
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() {
Yves Gerey665174f2018-06-19 15:03:05 +0200123 if (external_echo_path_ != NULL) {
124 delete[] external_echo_path_;
125 external_echo_path_ = NULL;
126 }
bjornv@google.comc4b939c2011-07-13 08:09:56 +0000127}
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) {
Yves Gerey665174f2018-06-19 15:03:05 +0200221 memset(audio->split_bands(capture)[band], 0,
aluebs776593b2016-03-15 14:04:58 -0700222 audio->num_frames_per_band() *
223 sizeof(audio->split_bands(capture)[band][0]));
224 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000225 }
peahdf3efa82015-11-28 12:35:15 -0800226 return AudioProcessing::kNoError;
niklase@google.com470e71d2011-07-07 08:21:25 +0000227}
228
229int EchoControlMobileImpl::Enable(bool enable) {
niklase@google.com470e71d2011-07-07 08:21:25 +0000230 // Ensure AEC and AECM are not both enabled.
peahdf3efa82015-11-28 12:35:15 -0800231 rtc::CritScope cs_render(crit_render_);
232 rtc::CritScope cs_capture(crit_capture_);
peah253534d2016-03-15 04:32:28 -0700233 RTC_DCHECK(stream_properties_);
niklase@google.com470e71d2011-07-07 08:21:25 +0000234
peahbb9edbd2016-03-10 12:54:25 -0800235 if (enable &&
peah253534d2016-03-15 04:32:28 -0700236 stream_properties_->sample_rate_hz > AudioProcessing::kSampleRate16kHz) {
peahbb9edbd2016-03-10 12:54:25 -0800237 return AudioProcessing::kBadSampleRateError;
238 }
239
240 if (enable && !enabled_) {
241 enabled_ = enable; // Must be set before Initialize() is called.
peah253534d2016-03-15 04:32:28 -0700242
243 // TODO(peah): Simplify once the Enable function has been removed from
244 // the public APM API.
245 Initialize(stream_properties_->sample_rate_hz,
246 stream_properties_->num_reverse_channels,
247 stream_properties_->num_output_channels);
peahbb9edbd2016-03-10 12:54:25 -0800248 } else {
249 enabled_ = enable;
250 }
251 return AudioProcessing::kNoError;
niklase@google.com470e71d2011-07-07 08:21:25 +0000252}
253
254bool EchoControlMobileImpl::is_enabled() const {
peahdf3efa82015-11-28 12:35:15 -0800255 rtc::CritScope cs(crit_capture_);
peahbb9edbd2016-03-10 12:54:25 -0800256 return enabled_;
niklase@google.com470e71d2011-07-07 08:21:25 +0000257}
258
259int EchoControlMobileImpl::set_routing_mode(RoutingMode mode) {
niklase@google.com470e71d2011-07-07 08:21:25 +0000260 if (MapSetting(mode) == -1) {
peahdf3efa82015-11-28 12:35:15 -0800261 return AudioProcessing::kBadParameterError;
niklase@google.com470e71d2011-07-07 08:21:25 +0000262 }
263
peahdf3efa82015-11-28 12:35:15 -0800264 {
265 rtc::CritScope cs(crit_capture_);
266 routing_mode_ = mode;
267 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000268 return Configure();
269}
270
Yves Gerey665174f2018-06-19 15:03:05 +0200271EchoControlMobile::RoutingMode EchoControlMobileImpl::routing_mode() const {
peahdf3efa82015-11-28 12:35:15 -0800272 rtc::CritScope cs(crit_capture_);
niklase@google.com470e71d2011-07-07 08:21:25 +0000273 return routing_mode_;
274}
275
276int EchoControlMobileImpl::enable_comfort_noise(bool enable) {
peahdf3efa82015-11-28 12:35:15 -0800277 {
278 rtc::CritScope cs(crit_capture_);
279 comfort_noise_enabled_ = enable;
280 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000281 return Configure();
282}
283
284bool EchoControlMobileImpl::is_comfort_noise_enabled() const {
peahdf3efa82015-11-28 12:35:15 -0800285 rtc::CritScope cs(crit_capture_);
niklase@google.com470e71d2011-07-07 08:21:25 +0000286 return comfort_noise_enabled_;
287}
288
bjornv@google.comc4b939c2011-07-13 08:09:56 +0000289int EchoControlMobileImpl::SetEchoPath(const void* echo_path,
ajm@google.com22e65152011-07-18 18:03:01 +0000290 size_t size_bytes) {
peahdf3efa82015-11-28 12:35:15 -0800291 {
292 rtc::CritScope cs_render(crit_render_);
293 rtc::CritScope cs_capture(crit_capture_);
294 if (echo_path == NULL) {
295 return AudioProcessing::kNullPointerError;
296 }
297 if (size_bytes != echo_path_size_bytes()) {
298 // Size mismatch
299 return AudioProcessing::kBadParameterError;
300 }
bjornv@google.comc4b939c2011-07-13 08:09:56 +0000301
peahdf3efa82015-11-28 12:35:15 -0800302 if (external_echo_path_ == NULL) {
303 external_echo_path_ = new unsigned char[size_bytes];
304 }
305 memcpy(external_echo_path_, echo_path, size_bytes);
bjornv@google.comc4b939c2011-07-13 08:09:56 +0000306 }
bjornv@google.comc4b939c2011-07-13 08:09:56 +0000307
peah253534d2016-03-15 04:32:28 -0700308 // TODO(peah): Simplify once the Enable function has been removed from
309 // the public APM API.
310 RTC_DCHECK(stream_properties_);
311 Initialize(stream_properties_->sample_rate_hz,
312 stream_properties_->num_reverse_channels,
313 stream_properties_->num_output_channels);
peahbb9edbd2016-03-10 12:54:25 -0800314 return AudioProcessing::kNoError;
bjornv@google.comc4b939c2011-07-13 08:09:56 +0000315}
316
317int EchoControlMobileImpl::GetEchoPath(void* echo_path,
ajm@google.com22e65152011-07-18 18:03:01 +0000318 size_t size_bytes) const {
peahdf3efa82015-11-28 12:35:15 -0800319 rtc::CritScope cs(crit_capture_);
bjornv@google.comc4b939c2011-07-13 08:09:56 +0000320 if (echo_path == NULL) {
peahdf3efa82015-11-28 12:35:15 -0800321 return AudioProcessing::kNullPointerError;
bjornv@google.comc4b939c2011-07-13 08:09:56 +0000322 }
ajm@google.com22e65152011-07-18 18:03:01 +0000323 if (size_bytes != echo_path_size_bytes()) {
bjornv@google.comc4b939c2011-07-13 08:09:56 +0000324 // Size mismatch
peahdf3efa82015-11-28 12:35:15 -0800325 return AudioProcessing::kBadParameterError;
bjornv@google.comc4b939c2011-07-13 08:09:56 +0000326 }
peahbb9edbd2016-03-10 12:54:25 -0800327 if (!enabled_) {
peahdf3efa82015-11-28 12:35:15 -0800328 return AudioProcessing::kNotEnabledError;
bjornv@google.comc4b939c2011-07-13 08:09:56 +0000329 }
330
331 // Get the echo path from the first channel
peahbb9edbd2016-03-10 12:54:25 -0800332 int32_t err =
333 WebRtcAecm_GetEchoPath(cancellers_[0]->state(), echo_path, size_bytes);
334 if (err != 0) {
peahfa6228e2015-11-16 16:27:42 -0800335 return MapError(err);
peahbb9edbd2016-03-10 12:54:25 -0800336 }
bjornv@google.comc4b939c2011-07-13 08:09:56 +0000337
peahdf3efa82015-11-28 12:35:15 -0800338 return AudioProcessing::kNoError;
bjornv@google.comc4b939c2011-07-13 08:09:56 +0000339}
340
peah253534d2016-03-15 04:32:28 -0700341void EchoControlMobileImpl::Initialize(int sample_rate_hz,
342 size_t num_reverse_channels,
343 size_t num_output_channels) {
peahbb9edbd2016-03-10 12:54:25 -0800344 rtc::CritScope cs_render(crit_render_);
345 rtc::CritScope cs_capture(crit_capture_);
peah253534d2016-03-15 04:32:28 -0700346
347 stream_properties_.reset(new StreamProperties(
348 sample_rate_hz, num_reverse_channels, num_output_channels));
349
peahbb9edbd2016-03-10 12:54:25 -0800350 if (!enabled_) {
351 return;
niklase@google.com470e71d2011-07-07 08:21:25 +0000352 }
353
Jonas Olsson645b0272018-02-15 15:16:27 +0100354 // AECM only supports 16 kHz or lower sample rates.
355 RTC_DCHECK_LE(stream_properties_->sample_rate_hz,
356 AudioProcessing::kSampleRate16kHz);
niklase@google.com470e71d2011-07-07 08:21:25 +0000357
peaha0624602016-10-25 04:45:24 -0700358 cancellers_.resize(
359 NumCancellersRequired(stream_properties_->num_output_channels,
360 stream_properties_->num_reverse_channels));
361
peahbb9edbd2016-03-10 12:54:25 -0800362 for (auto& canceller : cancellers_) {
363 if (!canceller) {
364 canceller.reset(new Canceller());
365 }
366 canceller->Initialize(sample_rate_hz, external_echo_path_,
367 echo_path_size_bytes());
peahfa6228e2015-11-16 16:27:42 -0800368 }
369
peahbb9edbd2016-03-10 12:54:25 -0800370 Configure();
niklase@google.com470e71d2011-07-07 08:21:25 +0000371}
372
peahbb9edbd2016-03-10 12:54:25 -0800373int EchoControlMobileImpl::Configure() {
peahdf3efa82015-11-28 12:35:15 -0800374 rtc::CritScope cs_render(crit_render_);
375 rtc::CritScope cs_capture(crit_capture_);
niklase@google.com470e71d2011-07-07 08:21:25 +0000376 AecmConfig config;
377 config.cngMode = comfort_noise_enabled_;
378 config.echoMode = MapSetting(routing_mode_);
peahbb9edbd2016-03-10 12:54:25 -0800379 int error = AudioProcessing::kNoError;
380 for (auto& canceller : cancellers_) {
381 int handle_error = WebRtcAecm_set_config(canceller->state(), config);
382 if (handle_error != AudioProcessing::kNoError) {
383 error = handle_error;
384 }
385 }
386 return error;
niklase@google.com470e71d2011-07-07 08:21:25 +0000387}
388
niklase@google.com470e71d2011-07-07 08:21:25 +0000389} // namespace webrtc