blob: 3bfb65f303252ca5b2fa799286866ed6aa252c34 [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>
Yves Gerey988cc082018-10-23 12:03:01 +020014#include <cstdint>
niklase@google.com470e71d2011-07-07 08:21:25 +000015
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020016#include "modules/audio_processing/aecm/echo_control_mobile.h"
17#include "modules/audio_processing/audio_buffer.h"
Yves Gerey988cc082018-10-23 12:03:01 +020018#include "modules/audio_processing/include/audio_processing.h"
19#include "rtc_base/checks.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020020#include "rtc_base/constructormagic.h"
niklase@google.com470e71d2011-07-07 08:21:25 +000021
22namespace webrtc {
23
niklase@google.com470e71d2011-07-07 08:21:25 +000024namespace {
Sam Zackrisson8c147b62018-09-28 12:40:47 +020025int16_t MapSetting(EchoControlMobileImpl::RoutingMode mode) {
niklase@google.com470e71d2011-07-07 08:21:25 +000026 switch (mode) {
Sam Zackrisson8c147b62018-09-28 12:40:47 +020027 case EchoControlMobileImpl::kQuietEarpieceOrHeadset:
niklase@google.com470e71d2011-07-07 08:21:25 +000028 return 0;
Sam Zackrisson8c147b62018-09-28 12:40:47 +020029 case EchoControlMobileImpl::kEarpiece:
niklase@google.com470e71d2011-07-07 08:21:25 +000030 return 1;
Sam Zackrisson8c147b62018-09-28 12:40:47 +020031 case EchoControlMobileImpl::kLoudEarpiece:
niklase@google.com470e71d2011-07-07 08:21:25 +000032 return 2;
Sam Zackrisson8c147b62018-09-28 12:40:47 +020033 case EchoControlMobileImpl::kSpeakerphone:
niklase@google.com470e71d2011-07-07 08:21:25 +000034 return 3;
Sam Zackrisson8c147b62018-09-28 12:40:47 +020035 case EchoControlMobileImpl::kLoudSpeakerphone:
niklase@google.com470e71d2011-07-07 08:21:25 +000036 return 4;
niklase@google.com470e71d2011-07-07 08:21:25 +000037 }
nisseeb4ca4e2017-01-12 02:24:27 -080038 RTC_NOTREACHED();
mflodman@webrtc.org657b2a42012-02-06 11:06:01 +000039 return -1;
niklase@google.com470e71d2011-07-07 08:21:25 +000040}
41
peahfa6228e2015-11-16 16:27:42 -080042AudioProcessing::Error MapError(int err) {
43 switch (err) {
44 case AECM_UNSUPPORTED_FUNCTION_ERROR:
45 return AudioProcessing::kUnsupportedFunctionError;
46 case AECM_NULL_POINTER_ERROR:
47 return AudioProcessing::kNullPointerError;
48 case AECM_BAD_PARAMETER_ERROR:
49 return AudioProcessing::kBadParameterError;
50 case AECM_BAD_PARAMETER_WARNING:
51 return AudioProcessing::kBadStreamParameterWarning;
52 default:
53 // AECM_UNSPECIFIED_ERROR
54 // AECM_UNINITIALIZED_ERROR
55 return AudioProcessing::kUnspecifiedError;
56 }
57}
niklase@google.com470e71d2011-07-07 08:21:25 +000058} // namespace
59
Sam Zackrisson8c147b62018-09-28 12:40:47 +020060size_t EchoControlMobileImpl::echo_path_size_bytes() {
peahbb9edbd2016-03-10 12:54:25 -080061 return WebRtcAecm_echo_path_size_bytes();
ajm@google.com22e65152011-07-18 18:03:01 +000062}
63
peah253534d2016-03-15 04:32:28 -070064struct EchoControlMobileImpl::StreamProperties {
65 StreamProperties() = delete;
66 StreamProperties(int sample_rate_hz,
67 size_t num_reverse_channels,
68 size_t num_output_channels)
69 : sample_rate_hz(sample_rate_hz),
70 num_reverse_channels(num_reverse_channels),
71 num_output_channels(num_output_channels) {}
72
73 int sample_rate_hz;
74 size_t num_reverse_channels;
75 size_t num_output_channels;
76};
77
peahbb9edbd2016-03-10 12:54:25 -080078class EchoControlMobileImpl::Canceller {
79 public:
80 Canceller() {
81 state_ = WebRtcAecm_Create();
82 RTC_CHECK(state_);
83 }
84
85 ~Canceller() {
86 RTC_DCHECK(state_);
87 WebRtcAecm_Free(state_);
88 }
89
90 void* state() {
91 RTC_DCHECK(state_);
92 return state_;
93 }
94
95 void Initialize(int sample_rate_hz,
96 unsigned char* external_echo_path,
97 size_t echo_path_size_bytes) {
98 RTC_DCHECK(state_);
99 int error = WebRtcAecm_Init(state_, sample_rate_hz);
100 RTC_DCHECK_EQ(AudioProcessing::kNoError, error);
101 if (external_echo_path != NULL) {
102 error = WebRtcAecm_InitEchoPath(state_, external_echo_path,
103 echo_path_size_bytes);
104 RTC_DCHECK_EQ(AudioProcessing::kNoError, error);
105 }
106 }
107
108 private:
109 void* state_;
110 RTC_DISALLOW_COPY_AND_ASSIGN(Canceller);
111};
112
peah253534d2016-03-15 04:32:28 -0700113EchoControlMobileImpl::EchoControlMobileImpl(rtc::CriticalSection* crit_render,
peahdf3efa82015-11-28 12:35:15 -0800114 rtc::CriticalSection* crit_capture)
peah253534d2016-03-15 04:32:28 -0700115 : crit_render_(crit_render),
peahdf3efa82015-11-28 12:35:15 -0800116 crit_capture_(crit_capture),
peahfa6228e2015-11-16 16:27:42 -0800117 routing_mode_(kSpeakerphone),
Sam Zackrissone507b0c2018-07-20 15:22:50 +0200118 comfort_noise_enabled_(false),
peaha0624602016-10-25 04:45:24 -0700119 external_echo_path_(NULL) {
peahdf3efa82015-11-28 12:35:15 -0800120 RTC_DCHECK(crit_render);
121 RTC_DCHECK(crit_capture);
122}
niklase@google.com470e71d2011-07-07 08:21:25 +0000123
bjornv@google.comc4b939c2011-07-13 08:09:56 +0000124EchoControlMobileImpl::~EchoControlMobileImpl() {
Yves Gerey665174f2018-06-19 15:03:05 +0200125 if (external_echo_path_ != NULL) {
126 delete[] external_echo_path_;
127 external_echo_path_ = NULL;
128 }
bjornv@google.comc4b939c2011-07-13 08:09:56 +0000129}
niklase@google.com470e71d2011-07-07 08:21:25 +0000130
peaha0624602016-10-25 04:45:24 -0700131void EchoControlMobileImpl::ProcessRenderAudio(
132 rtc::ArrayView<const int16_t> packed_render_audio) {
peahdf3efa82015-11-28 12:35:15 -0800133 rtc::CritScope cs_capture(crit_capture_);
peahbb9edbd2016-03-10 12:54:25 -0800134 if (!enabled_) {
peahfa6228e2015-11-16 16:27:42 -0800135 return;
136 }
137
peaha0624602016-10-25 04:45:24 -0700138 RTC_DCHECK(stream_properties_);
peahfa6228e2015-11-16 16:27:42 -0800139
peaha0624602016-10-25 04:45:24 -0700140 size_t buffer_index = 0;
141 size_t num_frames_per_band =
142 packed_render_audio.size() / (stream_properties_->num_output_channels *
143 stream_properties_->num_reverse_channels);
peahbb9edbd2016-03-10 12:54:25 -0800144
peaha0624602016-10-25 04:45:24 -0700145 for (auto& canceller : cancellers_) {
146 WebRtcAecm_BufferFarend(canceller->state(),
147 &packed_render_audio[buffer_index],
148 num_frames_per_band);
149
150 buffer_index += num_frames_per_band;
151 }
152}
153
154void EchoControlMobileImpl::PackRenderAudioBuffer(
155 const AudioBuffer* audio,
156 size_t num_output_channels,
157 size_t num_channels,
158 std::vector<int16_t>* packed_buffer) {
kwibergaf476c72016-11-28 15:21:39 -0800159 RTC_DCHECK_GE(160, audio->num_frames_per_band());
peaha0624602016-10-25 04:45:24 -0700160 RTC_DCHECK_EQ(num_channels, audio->num_channels());
161
162 // The ordering convention must be followed to pass to the correct AECM.
163 packed_buffer->clear();
164 int render_channel = 0;
165 for (size_t i = 0; i < num_output_channels; i++) {
166 for (size_t j = 0; j < audio->num_channels(); j++) {
167 // Buffer the samples in the render queue.
168 packed_buffer->insert(
169 packed_buffer->end(),
170 audio->split_bands_const(render_channel)[kBand0To8kHz],
171 (audio->split_bands_const(render_channel)[kBand0To8kHz] +
172 audio->num_frames_per_band()));
173 render_channel = (render_channel + 1) % audio->num_channels();
peahfa6228e2015-11-16 16:27:42 -0800174 }
175 }
176}
177
peaha0624602016-10-25 04:45:24 -0700178size_t EchoControlMobileImpl::NumCancellersRequired(
179 size_t num_output_channels,
180 size_t num_reverse_channels) {
181 return num_output_channels * num_reverse_channels;
182}
183
peah253534d2016-03-15 04:32:28 -0700184int EchoControlMobileImpl::ProcessCaptureAudio(AudioBuffer* audio,
185 int stream_delay_ms) {
peahdf3efa82015-11-28 12:35:15 -0800186 rtc::CritScope cs_capture(crit_capture_);
peahbb9edbd2016-03-10 12:54:25 -0800187 if (!enabled_) {
peahdf3efa82015-11-28 12:35:15 -0800188 return AudioProcessing::kNoError;
niklase@google.com470e71d2011-07-07 08:21:25 +0000189 }
190
peah253534d2016-03-15 04:32:28 -0700191 RTC_DCHECK(stream_properties_);
kwibergaf476c72016-11-28 15:21:39 -0800192 RTC_DCHECK_GE(160, audio->num_frames_per_band());
peah253534d2016-03-15 04:32:28 -0700193 RTC_DCHECK_EQ(audio->num_channels(), stream_properties_->num_output_channels);
194 RTC_DCHECK_GE(cancellers_.size(), stream_properties_->num_reverse_channels *
195 audio->num_channels());
niklase@google.com470e71d2011-07-07 08:21:25 +0000196
peahdf3efa82015-11-28 12:35:15 -0800197 int err = AudioProcessing::kNoError;
niklase@google.com470e71d2011-07-07 08:21:25 +0000198
199 // The ordering convention must be followed to pass to the correct AECM.
200 size_t handle_index = 0;
peahbb9edbd2016-03-10 12:54:25 -0800201 for (size_t capture = 0; capture < audio->num_channels(); ++capture) {
niklase@google.com470e71d2011-07-07 08:21:25 +0000202 // TODO(ajm): improve how this works, possibly inside AECM.
203 // This is kind of hacked up.
peahbb9edbd2016-03-10 12:54:25 -0800204 const int16_t* noisy = audio->low_pass_reference(capture);
205 const int16_t* clean = audio->split_bands_const(capture)[kBand0To8kHz];
niklase@google.com470e71d2011-07-07 08:21:25 +0000206 if (noisy == NULL) {
207 noisy = clean;
208 clean = NULL;
209 }
peah253534d2016-03-15 04:32:28 -0700210 for (size_t render = 0; render < stream_properties_->num_reverse_channels;
211 ++render) {
peahbb9edbd2016-03-10 12:54:25 -0800212 err = WebRtcAecm_Process(cancellers_[handle_index]->state(), noisy, clean,
213 audio->split_bands(capture)[kBand0To8kHz],
peah253534d2016-03-15 04:32:28 -0700214 audio->num_frames_per_band(), stream_delay_ms);
niklase@google.com470e71d2011-07-07 08:21:25 +0000215
peahbb9edbd2016-03-10 12:54:25 -0800216 if (err != AudioProcessing::kNoError) {
peahfa6228e2015-11-16 16:27:42 -0800217 return MapError(err);
peahbb9edbd2016-03-10 12:54:25 -0800218 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000219
peahbb9edbd2016-03-10 12:54:25 -0800220 ++handle_index;
niklase@google.com470e71d2011-07-07 08:21:25 +0000221 }
aluebs776593b2016-03-15 14:04:58 -0700222 for (size_t band = 1u; band < audio->num_bands(); ++band) {
Yves Gerey665174f2018-06-19 15:03:05 +0200223 memset(audio->split_bands(capture)[band], 0,
aluebs776593b2016-03-15 14:04:58 -0700224 audio->num_frames_per_band() *
225 sizeof(audio->split_bands(capture)[band][0]));
226 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000227 }
peahdf3efa82015-11-28 12:35:15 -0800228 return AudioProcessing::kNoError;
niklase@google.com470e71d2011-07-07 08:21:25 +0000229}
230
231int EchoControlMobileImpl::Enable(bool enable) {
niklase@google.com470e71d2011-07-07 08:21:25 +0000232 // Ensure AEC and AECM are not both enabled.
peahdf3efa82015-11-28 12:35:15 -0800233 rtc::CritScope cs_render(crit_render_);
234 rtc::CritScope cs_capture(crit_capture_);
peah253534d2016-03-15 04:32:28 -0700235 RTC_DCHECK(stream_properties_);
niklase@google.com470e71d2011-07-07 08:21:25 +0000236
peahbb9edbd2016-03-10 12:54:25 -0800237 if (enable &&
peah253534d2016-03-15 04:32:28 -0700238 stream_properties_->sample_rate_hz > AudioProcessing::kSampleRate16kHz) {
peahbb9edbd2016-03-10 12:54:25 -0800239 return AudioProcessing::kBadSampleRateError;
240 }
241
242 if (enable && !enabled_) {
243 enabled_ = enable; // Must be set before Initialize() is called.
peah253534d2016-03-15 04:32:28 -0700244
245 // TODO(peah): Simplify once the Enable function has been removed from
246 // the public APM API.
247 Initialize(stream_properties_->sample_rate_hz,
248 stream_properties_->num_reverse_channels,
249 stream_properties_->num_output_channels);
peahbb9edbd2016-03-10 12:54:25 -0800250 } else {
251 enabled_ = enable;
252 }
253 return AudioProcessing::kNoError;
niklase@google.com470e71d2011-07-07 08:21:25 +0000254}
255
256bool EchoControlMobileImpl::is_enabled() const {
peahdf3efa82015-11-28 12:35:15 -0800257 rtc::CritScope cs(crit_capture_);
peahbb9edbd2016-03-10 12:54:25 -0800258 return enabled_;
niklase@google.com470e71d2011-07-07 08:21:25 +0000259}
260
261int EchoControlMobileImpl::set_routing_mode(RoutingMode mode) {
niklase@google.com470e71d2011-07-07 08:21:25 +0000262 if (MapSetting(mode) == -1) {
peahdf3efa82015-11-28 12:35:15 -0800263 return AudioProcessing::kBadParameterError;
niklase@google.com470e71d2011-07-07 08:21:25 +0000264 }
265
peahdf3efa82015-11-28 12:35:15 -0800266 {
267 rtc::CritScope cs(crit_capture_);
268 routing_mode_ = mode;
269 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000270 return Configure();
271}
272
Sam Zackrisson8c147b62018-09-28 12:40:47 +0200273EchoControlMobileImpl::RoutingMode EchoControlMobileImpl::routing_mode() 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
Jonas Olsson645b0272018-02-15 15:16:27 +0100356 // AECM only supports 16 kHz or lower sample rates.
357 RTC_DCHECK_LE(stream_properties_->sample_rate_hz,
358 AudioProcessing::kSampleRate16kHz);
niklase@google.com470e71d2011-07-07 08:21:25 +0000359
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