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 <cstdint>
16
17 #include "modules/audio_processing/aecm/echo_control_mobile.h"
18 #include "modules/audio_processing/audio_buffer.h"
19 #include "modules/audio_processing/include/audio_processing.h"
20 #include "rtc_base/checks.h"
21 #include "rtc_base/constructor_magic.h"
22
23 namespace webrtc {
24
25 namespace {
MapSetting(EchoControlMobileImpl::RoutingMode mode)26 int16_t MapSetting(EchoControlMobileImpl::RoutingMode mode) {
27 switch (mode) {
28 case EchoControlMobileImpl::kQuietEarpieceOrHeadset:
29 return 0;
30 case EchoControlMobileImpl::kEarpiece:
31 return 1;
32 case EchoControlMobileImpl::kLoudEarpiece:
33 return 2;
34 case EchoControlMobileImpl::kSpeakerphone:
35 return 3;
36 case EchoControlMobileImpl::kLoudSpeakerphone:
37 return 4;
38 }
39 RTC_NOTREACHED();
40 return -1;
41 }
42
MapError(int err)43 AudioProcessing::Error MapError(int err) {
44 switch (err) {
45 case AECM_UNSUPPORTED_FUNCTION_ERROR:
46 return AudioProcessing::kUnsupportedFunctionError;
47 case AECM_NULL_POINTER_ERROR:
48 return AudioProcessing::kNullPointerError;
49 case AECM_BAD_PARAMETER_ERROR:
50 return AudioProcessing::kBadParameterError;
51 case AECM_BAD_PARAMETER_WARNING:
52 return AudioProcessing::kBadStreamParameterWarning;
53 default:
54 // AECM_UNSPECIFIED_ERROR
55 // AECM_UNINITIALIZED_ERROR
56 return AudioProcessing::kUnspecifiedError;
57 }
58 }
59
60 } // namespace
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)93 void Initialize(int sample_rate_hz) {
94 RTC_DCHECK(state_);
95 int error = WebRtcAecm_Init(state_, sample_rate_hz);
96 RTC_DCHECK_EQ(AudioProcessing::kNoError, error);
97 }
98
99 private:
100 void* state_;
101 RTC_DISALLOW_COPY_AND_ASSIGN(Canceller);
102 };
103
EchoControlMobileImpl()104 EchoControlMobileImpl::EchoControlMobileImpl()
105 : routing_mode_(kSpeakerphone), comfort_noise_enabled_(false) {}
106
~EchoControlMobileImpl()107 EchoControlMobileImpl::~EchoControlMobileImpl() {}
108
ProcessRenderAudio(rtc::ArrayView<const int16_t> packed_render_audio)109 void EchoControlMobileImpl::ProcessRenderAudio(
110 rtc::ArrayView<const int16_t> packed_render_audio) {
111 RTC_DCHECK(stream_properties_);
112
113 size_t buffer_index = 0;
114 size_t num_frames_per_band =
115 packed_render_audio.size() / (stream_properties_->num_output_channels *
116 stream_properties_->num_reverse_channels);
117
118 for (auto& canceller : cancellers_) {
119 WebRtcAecm_BufferFarend(canceller->state(),
120 &packed_render_audio[buffer_index],
121 num_frames_per_band);
122
123 buffer_index += num_frames_per_band;
124 }
125 }
126
PackRenderAudioBuffer(const AudioBuffer * audio,size_t num_output_channels,size_t num_channels,std::vector<int16_t> * packed_buffer)127 void EchoControlMobileImpl::PackRenderAudioBuffer(
128 const AudioBuffer* audio,
129 size_t num_output_channels,
130 size_t num_channels,
131 std::vector<int16_t>* packed_buffer) {
132 RTC_DCHECK_GE(AudioBuffer::kMaxSplitFrameLength,
133 audio->num_frames_per_band());
134 RTC_DCHECK_EQ(num_channels, audio->num_channels());
135
136 // The ordering convention must be followed to pass to the correct AECM.
137 packed_buffer->clear();
138 int render_channel = 0;
139 for (size_t i = 0; i < num_output_channels; i++) {
140 for (size_t j = 0; j < audio->num_channels(); j++) {
141 std::array<int16_t, AudioBuffer::kMaxSplitFrameLength> data_to_buffer;
142 FloatS16ToS16(audio->split_bands_const(render_channel)[kBand0To8kHz],
143 audio->num_frames_per_band(), data_to_buffer.data());
144
145 // Buffer the samples in the render queue.
146 packed_buffer->insert(
147 packed_buffer->end(), data_to_buffer.data(),
148 data_to_buffer.data() + audio->num_frames_per_band());
149 render_channel = (render_channel + 1) % audio->num_channels();
150 }
151 }
152 }
153
NumCancellersRequired(size_t num_output_channels,size_t num_reverse_channels)154 size_t EchoControlMobileImpl::NumCancellersRequired(
155 size_t num_output_channels,
156 size_t num_reverse_channels) {
157 return num_output_channels * num_reverse_channels;
158 }
159
ProcessCaptureAudio(AudioBuffer * audio,int stream_delay_ms)160 int EchoControlMobileImpl::ProcessCaptureAudio(AudioBuffer* audio,
161 int stream_delay_ms) {
162 RTC_DCHECK(stream_properties_);
163 RTC_DCHECK_GE(160, audio->num_frames_per_band());
164 RTC_DCHECK_EQ(audio->num_channels(), stream_properties_->num_output_channels);
165 RTC_DCHECK_GE(cancellers_.size(), stream_properties_->num_reverse_channels *
166 audio->num_channels());
167
168 int err = AudioProcessing::kNoError;
169
170 // The ordering convention must be followed to pass to the correct AECM.
171 size_t handle_index = 0;
172 for (size_t capture = 0; capture < audio->num_channels(); ++capture) {
173 // TODO(ajm): improve how this works, possibly inside AECM.
174 // This is kind of hacked up.
175 RTC_DCHECK_LT(capture, low_pass_reference_.size());
176 const int16_t* noisy =
177 reference_copied_ ? low_pass_reference_[capture].data() : nullptr;
178
179 RTC_DCHECK_GE(AudioBuffer::kMaxSplitFrameLength,
180 audio->num_frames_per_band());
181
182 std::array<int16_t, AudioBuffer::kMaxSplitFrameLength> split_bands_data;
183 int16_t* split_bands = split_bands_data.data();
184 const int16_t* clean = split_bands_data.data();
185 if (audio->split_bands(capture)[kBand0To8kHz]) {
186 FloatS16ToS16(audio->split_bands(capture)[kBand0To8kHz],
187 audio->num_frames_per_band(), split_bands_data.data());
188 } else {
189 clean = nullptr;
190 split_bands = nullptr;
191 }
192
193 if (noisy == NULL) {
194 noisy = clean;
195 clean = NULL;
196 }
197 for (size_t render = 0; render < stream_properties_->num_reverse_channels;
198 ++render) {
199 err = WebRtcAecm_Process(cancellers_[handle_index]->state(), noisy, clean,
200 split_bands, audio->num_frames_per_band(),
201 stream_delay_ms);
202
203 if (split_bands) {
204 S16ToFloatS16(split_bands, audio->num_frames_per_band(),
205 audio->split_bands(capture)[kBand0To8kHz]);
206 }
207
208 if (err != AudioProcessing::kNoError) {
209 return MapError(err);
210 }
211
212 ++handle_index;
213 }
214 for (size_t band = 1u; band < audio->num_bands(); ++band) {
215 memset(audio->split_bands_f(capture)[band], 0,
216 audio->num_frames_per_band() *
217 sizeof(audio->split_bands_f(capture)[band][0]));
218 }
219 }
220 return AudioProcessing::kNoError;
221 }
222
set_routing_mode(RoutingMode mode)223 int EchoControlMobileImpl::set_routing_mode(RoutingMode mode) {
224 if (MapSetting(mode) == -1) {
225 return AudioProcessing::kBadParameterError;
226 }
227 routing_mode_ = mode;
228 return Configure();
229 }
230
routing_mode() const231 EchoControlMobileImpl::RoutingMode EchoControlMobileImpl::routing_mode() const {
232 return routing_mode_;
233 }
234
enable_comfort_noise(bool enable)235 int EchoControlMobileImpl::enable_comfort_noise(bool enable) {
236 comfort_noise_enabled_ = enable;
237 return Configure();
238 }
239
is_comfort_noise_enabled() const240 bool EchoControlMobileImpl::is_comfort_noise_enabled() const {
241 return comfort_noise_enabled_;
242 }
243
Initialize(int sample_rate_hz,size_t num_reverse_channels,size_t num_output_channels)244 void EchoControlMobileImpl::Initialize(int sample_rate_hz,
245 size_t num_reverse_channels,
246 size_t num_output_channels) {
247 low_pass_reference_.resize(num_output_channels);
248 for (auto& reference : low_pass_reference_) {
249 reference.fill(0);
250 }
251
252 stream_properties_.reset(new StreamProperties(
253 sample_rate_hz, num_reverse_channels, num_output_channels));
254
255 // AECM only supports 16 kHz or lower sample rates.
256 RTC_DCHECK_LE(stream_properties_->sample_rate_hz,
257 AudioProcessing::kSampleRate16kHz);
258
259 cancellers_.resize(
260 NumCancellersRequired(stream_properties_->num_output_channels,
261 stream_properties_->num_reverse_channels));
262
263 for (auto& canceller : cancellers_) {
264 if (!canceller) {
265 canceller.reset(new Canceller());
266 }
267 canceller->Initialize(sample_rate_hz);
268 }
269 Configure();
270 }
271
Configure()272 int EchoControlMobileImpl::Configure() {
273 AecmConfig config;
274 config.cngMode = comfort_noise_enabled_;
275 config.echoMode = MapSetting(routing_mode_);
276 int error = AudioProcessing::kNoError;
277 for (auto& canceller : cancellers_) {
278 int handle_error = WebRtcAecm_set_config(canceller->state(), config);
279 if (handle_error != AudioProcessing::kNoError) {
280 error = handle_error;
281 }
282 }
283 return error;
284 }
285
286 } // namespace webrtc
287