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 "modules/audio_processing/echo_control_mobile_impl.h"
12 
13 #include <string.h>
14 
15 #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"
19 
20 namespace webrtc {
21 
22 namespace {
MapSetting(EchoControlMobile::RoutingMode mode)23 int16_t MapSetting(EchoControlMobile::RoutingMode mode) {
24   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;
35   }
36   RTC_NOTREACHED();
37   return -1;
38 }
39 
MapError(int err)40 AudioProcessing::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 }
56 }  // namespace
57 
echo_path_size_bytes()58 size_t EchoControlMobile::echo_path_size_bytes() {
59   return WebRtcAecm_echo_path_size_bytes();
60 }
61 
62 struct EchoControlMobileImpl::StreamProperties {
63   StreamProperties() = delete;
StreamPropertieswebrtc::EchoControlMobileImpl::StreamProperties64   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 
76 class EchoControlMobileImpl::Canceller {
77  public:
Canceller()78   Canceller() {
79     state_ = WebRtcAecm_Create();
80     RTC_CHECK(state_);
81   }
82 
~Canceller()83   ~Canceller() {
84     RTC_DCHECK(state_);
85     WebRtcAecm_Free(state_);
86   }
87 
state()88   void* state() {
89     RTC_DCHECK(state_);
90     return state_;
91   }
92 
Initialize(int sample_rate_hz,unsigned char * external_echo_path,size_t echo_path_size_bytes)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 
EchoControlMobileImpl(rtc::CriticalSection * crit_render,rtc::CriticalSection * crit_capture)111 EchoControlMobileImpl::EchoControlMobileImpl(rtc::CriticalSection* crit_render,
112                                              rtc::CriticalSection* crit_capture)
113     : crit_render_(crit_render),
114       crit_capture_(crit_capture),
115       routing_mode_(kSpeakerphone),
116       comfort_noise_enabled_(true),
117       external_echo_path_(NULL) {
118   RTC_DCHECK(crit_render);
119   RTC_DCHECK(crit_capture);
120 }
121 
~EchoControlMobileImpl()122 EchoControlMobileImpl::~EchoControlMobileImpl() {
123     if (external_echo_path_ != NULL) {
124       delete [] external_echo_path_;
125       external_echo_path_ = NULL;
126     }
127 }
128 
ProcessRenderAudio(rtc::ArrayView<const int16_t> packed_render_audio)129 void EchoControlMobileImpl::ProcessRenderAudio(
130     rtc::ArrayView<const int16_t> packed_render_audio) {
131   rtc::CritScope cs_capture(crit_capture_);
132   if (!enabled_) {
133     return;
134   }
135 
136   RTC_DCHECK(stream_properties_);
137 
138   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);
142 
143   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 
PackRenderAudioBuffer(const AudioBuffer * audio,size_t num_output_channels,size_t num_channels,std::vector<int16_t> * packed_buffer)152 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   RTC_DCHECK_GE(160, audio->num_frames_per_band());
158   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();
172     }
173   }
174 }
175 
NumCancellersRequired(size_t num_output_channels,size_t num_reverse_channels)176 size_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 
ProcessCaptureAudio(AudioBuffer * audio,int stream_delay_ms)182 int EchoControlMobileImpl::ProcessCaptureAudio(AudioBuffer* audio,
183                                                int stream_delay_ms) {
184   rtc::CritScope cs_capture(crit_capture_);
185   if (!enabled_) {
186     return AudioProcessing::kNoError;
187   }
188 
189   RTC_DCHECK(stream_properties_);
190   RTC_DCHECK_GE(160, audio->num_frames_per_band());
191   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());
194 
195   int err = AudioProcessing::kNoError;
196 
197   // The ordering convention must be followed to pass to the correct AECM.
198   size_t handle_index = 0;
199   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     const int16_t* noisy = audio->low_pass_reference(capture);
203     const int16_t* clean = audio->split_bands_const(capture)[kBand0To8kHz];
204     if (noisy == NULL) {
205       noisy = clean;
206       clean = NULL;
207     }
208     for (size_t render = 0; render < stream_properties_->num_reverse_channels;
209          ++render) {
210       err = WebRtcAecm_Process(cancellers_[handle_index]->state(), noisy, clean,
211                                audio->split_bands(capture)[kBand0To8kHz],
212                                audio->num_frames_per_band(), stream_delay_ms);
213 
214       if (err != AudioProcessing::kNoError) {
215         return MapError(err);
216       }
217 
218       ++handle_index;
219     }
220     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     }
226   }
227   return AudioProcessing::kNoError;
228 }
229 
Enable(bool enable)230 int EchoControlMobileImpl::Enable(bool enable) {
231   // Ensure AEC and AECM are not both enabled.
232   rtc::CritScope cs_render(crit_render_);
233   rtc::CritScope cs_capture(crit_capture_);
234   RTC_DCHECK(stream_properties_);
235 
236   if (enable &&
237       stream_properties_->sample_rate_hz > AudioProcessing::kSampleRate16kHz) {
238     return AudioProcessing::kBadSampleRateError;
239   }
240 
241   if (enable && !enabled_) {
242     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     Initialize(stream_properties_->sample_rate_hz,
247                stream_properties_->num_reverse_channels,
248                stream_properties_->num_output_channels);
249   } else {
250     enabled_ = enable;
251   }
252   return AudioProcessing::kNoError;
253 }
254 
is_enabled() const255 bool EchoControlMobileImpl::is_enabled() const {
256   rtc::CritScope cs(crit_capture_);
257   return enabled_;
258 }
259 
set_routing_mode(RoutingMode mode)260 int EchoControlMobileImpl::set_routing_mode(RoutingMode mode) {
261   if (MapSetting(mode) == -1) {
262     return AudioProcessing::kBadParameterError;
263   }
264 
265   {
266     rtc::CritScope cs(crit_capture_);
267     routing_mode_ = mode;
268   }
269   return Configure();
270 }
271 
routing_mode() const272 EchoControlMobile::RoutingMode EchoControlMobileImpl::routing_mode()
273     const {
274   rtc::CritScope cs(crit_capture_);
275   return routing_mode_;
276 }
277 
enable_comfort_noise(bool enable)278 int EchoControlMobileImpl::enable_comfort_noise(bool enable) {
279   {
280     rtc::CritScope cs(crit_capture_);
281     comfort_noise_enabled_ = enable;
282   }
283   return Configure();
284 }
285 
is_comfort_noise_enabled() const286 bool EchoControlMobileImpl::is_comfort_noise_enabled() const {
287   rtc::CritScope cs(crit_capture_);
288   return comfort_noise_enabled_;
289 }
290 
SetEchoPath(const void * echo_path,size_t size_bytes)291 int EchoControlMobileImpl::SetEchoPath(const void* echo_path,
292                                        size_t size_bytes) {
293   {
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     }
303 
304     if (external_echo_path_ == NULL) {
305       external_echo_path_ = new unsigned char[size_bytes];
306     }
307     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   RTC_DCHECK(stream_properties_);
313   Initialize(stream_properties_->sample_rate_hz,
314              stream_properties_->num_reverse_channels,
315              stream_properties_->num_output_channels);
316   return AudioProcessing::kNoError;
317 }
318 
GetEchoPath(void * echo_path,size_t size_bytes) const319 int EchoControlMobileImpl::GetEchoPath(void* echo_path,
320                                        size_t size_bytes) const {
321   rtc::CritScope cs(crit_capture_);
322   if (echo_path == NULL) {
323     return AudioProcessing::kNullPointerError;
324   }
325   if (size_bytes != echo_path_size_bytes()) {
326     // Size mismatch
327     return AudioProcessing::kBadParameterError;
328   }
329   if (!enabled_) {
330     return AudioProcessing::kNotEnabledError;
331   }
332 
333   // Get the echo path from the first channel
334   int32_t err =
335       WebRtcAecm_GetEchoPath(cancellers_[0]->state(), echo_path, size_bytes);
336   if (err != 0) {
337     return MapError(err);
338   }
339 
340   return AudioProcessing::kNoError;
341 }
342 
Initialize(int sample_rate_hz,size_t num_reverse_channels,size_t num_output_channels)343 void EchoControlMobileImpl::Initialize(int sample_rate_hz,
344                                        size_t num_reverse_channels,
345                                        size_t num_output_channels) {
346   rtc::CritScope cs_render(crit_render_);
347   rtc::CritScope cs_capture(crit_capture_);
348 
349   stream_properties_.reset(new StreamProperties(
350       sample_rate_hz, num_reverse_channels, num_output_channels));
351 
352   if (!enabled_) {
353     return;
354   }
355 
356   if (stream_properties_->sample_rate_hz > AudioProcessing::kSampleRate16kHz) {
357     RTC_LOG(LS_ERROR) << "AECM only supports 16 kHz or lower sample rates";
358   }
359 
360   cancellers_.resize(
361       NumCancellersRequired(stream_properties_->num_output_channels,
362                             stream_properties_->num_reverse_channels));
363 
364   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());
370   }
371 
372   Configure();
373 }
374 
Configure()375 int EchoControlMobileImpl::Configure() {
376   rtc::CritScope cs_render(crit_render_);
377   rtc::CritScope cs_capture(crit_capture_);
378   AecmConfig config;
379   config.cngMode = comfort_noise_enabled_;
380   config.echoMode = MapSetting(routing_mode_);
381   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;
389 }
390 
391 }  // namespace webrtc
392