1 // Copyright 2018 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <gtest/gtest.h>
6 #include <stdio.h>
7
8 extern "C" {
9 #include "cras_apm_list.h"
10 #include "cras_audio_area.h"
11 #include "cras_dsp_pipeline.h"
12 #include "cras_iodev.h"
13 #include "cras_iodev_list.h"
14 #include "cras_types.h"
15 #include "float_buffer.h"
16 #include "webrtc_apm.h"
17 }
18
19 #define FILENAME_TEMPLATE "ApmTest.XXXXXX"
20
21 namespace {
22
23 static void* stream_ptr = reinterpret_cast<void*>(0x123);
24 static void* dev_ptr = reinterpret_cast<void*>(0x345);
25 static void* dev_ptr2 = reinterpret_cast<void*>(0x678);
26 static struct cras_apm_list* list;
27 static struct cras_audio_area fake_audio_area;
28 static unsigned int dsp_util_interleave_frames;
29 static unsigned int webrtc_apm_process_stream_f_called;
30 static unsigned int webrtc_apm_process_reverse_stream_f_called;
31 static device_enabled_callback_t device_enabled_callback_val;
32 static struct ext_dsp_module* ext_dsp_module_value;
33 static struct cras_ionode fake_node;
34 static struct cras_iodev fake_iodev;
35 static int webrtc_apm_create_called;
36 static bool cras_iodev_is_aec_use_case_ret;
37 static dictionary* webrtc_apm_create_aec_ini_val = NULL;
38 static dictionary* webrtc_apm_create_apm_ini_val = NULL;
39
TEST(ApmList,ApmListCreate)40 TEST(ApmList, ApmListCreate) {
41 list = cras_apm_list_create(stream_ptr, 0);
42 EXPECT_EQ((void*)NULL, list);
43
44 list = cras_apm_list_create(stream_ptr, APM_ECHO_CANCELLATION);
45 EXPECT_NE((void*)NULL, list);
46 EXPECT_EQ(APM_ECHO_CANCELLATION, cras_apm_list_get_effects(list));
47
48 cras_apm_list_destroy(list);
49 }
50
prepare_tempdir()51 static char* prepare_tempdir() {
52 char dirname[sizeof(FILENAME_TEMPLATE) + 1];
53 char filename[64];
54 char* tempdir;
55 FILE* fp;
56
57 strcpy(dirname, FILENAME_TEMPLATE);
58 tempdir = mkdtemp(dirname);
59 snprintf(filename, 64, "%s/apm.ini", tempdir);
60 fp = fopen(filename, "w");
61 fprintf(fp, "%s", "[foo]\n");
62 fclose(fp);
63 fp = NULL;
64 snprintf(filename, 64, "%s/aec.ini", tempdir);
65 fp = fopen(filename, "w");
66 fprintf(fp, "%s", "[bar]\n");
67 fclose(fp);
68 fp = NULL;
69 return strdup(tempdir);
70 }
71
delete_tempdir(char * dir)72 static void delete_tempdir(char* dir) {
73 char filename[64];
74
75 snprintf(filename, 64, "%s/apm.ini", dir);
76 unlink(filename);
77 snprintf(filename, 64, "%s/aec.ini", dir);
78 unlink(filename);
79 rmdir(dir);
80 }
81
init_channel_layout(struct cras_audio_format * fmt)82 static void init_channel_layout(struct cras_audio_format* fmt) {
83 int i;
84 for (i = 0; i < CRAS_CH_MAX; i++)
85 fmt->channel_layout[i] = -1;
86 }
87
TEST(ApmList,AddApmInputDevUnuseFirstChannel)88 TEST(ApmList, AddApmInputDevUnuseFirstChannel) {
89 struct cras_audio_format fmt;
90 struct cras_audio_format* val;
91 struct cras_apm* apm;
92 int ch;
93 const int num_test_casts = 9;
94 int test_layouts[num_test_casts][CRAS_CH_MAX] = {
95 {0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
96 {0, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1},
97 {0, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
98 {1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
99 {1, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1},
100 {2, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1},
101 {2, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1},
102 {3, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1},
103 {3, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1}};
104 int test_num_channels[num_test_casts] = {1, 2, 2, 2, 2, 3, 4, 4, 4};
105
106 fmt.frame_rate = 48000;
107 fmt.format = SND_PCM_FORMAT_S16_LE;
108
109 cras_apm_list_init("");
110 list = cras_apm_list_create(stream_ptr, APM_ECHO_CANCELLATION);
111 EXPECT_NE((void*)NULL, list);
112
113 for (int i = 0; i < num_test_casts; i++) {
114 fmt.num_channels = test_num_channels[i];
115 init_channel_layout(&fmt);
116 for (ch = 0; ch < CRAS_CH_MAX; ch++)
117 fmt.channel_layout[ch] = test_layouts[i][ch];
118
119 /* Input dev is of aec use case. */
120 apm = cras_apm_list_add_apm(list, dev_ptr, &fmt, 1);
121 EXPECT_NE((void*)NULL, apm);
122
123 /* Assert that the post-processing format never has an unset
124 * first channel in the layout. */
125 bool first_channel_found_in_layout = 0;
126 val = cras_apm_list_get_format(apm);
127 for (ch = 0; ch < CRAS_CH_MAX; ch++)
128 if (0 == val->channel_layout[ch])
129 first_channel_found_in_layout = 1;
130
131 EXPECT_EQ(1, first_channel_found_in_layout);
132
133 cras_apm_list_remove_apm(list, dev_ptr);
134 }
135
136 cras_apm_list_destroy(list);
137 cras_apm_list_deinit();
138 }
139
TEST(ApmList,AddRemoveApm)140 TEST(ApmList, AddRemoveApm) {
141 struct cras_audio_format fmt;
142 char* dir;
143
144 fmt.num_channels = 2;
145 fmt.frame_rate = 48000;
146 fmt.format = SND_PCM_FORMAT_S16_LE;
147 fake_iodev.active_node = &fake_node;
148 fake_node.type = CRAS_NODE_TYPE_INTERNAL_SPEAKER;
149
150 dir = prepare_tempdir();
151 cras_apm_list_init(dir);
152 cras_iodev_is_aec_use_case_ret = 1;
153
154 list = cras_apm_list_create(stream_ptr, APM_ECHO_CANCELLATION);
155 EXPECT_NE((void*)NULL, list);
156
157 /* Input dev is of aec use case. */
158 EXPECT_NE((void*)NULL, cras_apm_list_add_apm(list, dev_ptr, &fmt, 1));
159 EXPECT_NE((void*)NULL, webrtc_apm_create_aec_ini_val);
160 EXPECT_NE((void*)NULL, webrtc_apm_create_apm_ini_val);
161 EXPECT_EQ((void*)NULL, cras_apm_list_get_active_apm(stream_ptr, dev_ptr));
162
163 cras_apm_list_start_apm(list, dev_ptr);
164 EXPECT_NE((void*)NULL, cras_apm_list_get_active_apm(stream_ptr, dev_ptr));
165 EXPECT_EQ((void*)NULL, cras_apm_list_get_active_apm(stream_ptr, dev_ptr2));
166
167 /* Input dev is not of aec use case. */
168 EXPECT_NE((void*)NULL, cras_apm_list_add_apm(list, dev_ptr2, &fmt, 0));
169 EXPECT_EQ((void*)NULL, webrtc_apm_create_aec_ini_val);
170 EXPECT_EQ((void*)NULL, webrtc_apm_create_apm_ini_val);
171 cras_apm_list_start_apm(list, dev_ptr2);
172 cras_apm_list_stop_apm(list, dev_ptr);
173
174 EXPECT_EQ((void*)NULL, cras_apm_list_get_active_apm(stream_ptr, dev_ptr));
175 EXPECT_NE((void*)NULL, cras_apm_list_get_active_apm(stream_ptr, dev_ptr2));
176
177 cras_apm_list_stop_apm(list, dev_ptr2);
178 cras_apm_list_remove_apm(list, dev_ptr);
179 cras_apm_list_remove_apm(list, dev_ptr2);
180
181 cras_apm_list_destroy(list);
182 cras_apm_list_deinit();
183 delete_tempdir(dir);
184 free(dir);
185 }
186
TEST(ApmList,OutputTypeNotAecUseCase)187 TEST(ApmList, OutputTypeNotAecUseCase) {
188 struct cras_audio_format fmt;
189 char* dir;
190
191 fmt.num_channels = 2;
192 fmt.frame_rate = 48000;
193 fmt.format = SND_PCM_FORMAT_S16_LE;
194 fake_iodev.active_node = &fake_node;
195
196 dir = prepare_tempdir();
197 cras_apm_list_init(dir);
198
199 list = cras_apm_list_create(stream_ptr, APM_ECHO_CANCELLATION);
200 EXPECT_NE((void*)NULL, list);
201
202 /* Output device is of aec use case. */
203 cras_iodev_is_aec_use_case_ret = 1;
204 EXPECT_NE((void*)NULL, cras_apm_list_add_apm(list, dev_ptr, &fmt, 1));
205 EXPECT_NE((void*)NULL, webrtc_apm_create_aec_ini_val);
206 EXPECT_NE((void*)NULL, webrtc_apm_create_apm_ini_val);
207 cras_apm_list_remove_apm(list, dev_ptr);
208
209 /* Output device is not of aec use case. */
210 cras_iodev_is_aec_use_case_ret = 0;
211 EXPECT_NE((void*)NULL, cras_apm_list_add_apm(list, dev_ptr, &fmt, 1));
212 EXPECT_EQ((void*)NULL, webrtc_apm_create_aec_ini_val);
213 EXPECT_EQ((void*)NULL, webrtc_apm_create_apm_ini_val);
214 cras_apm_list_remove_apm(list, dev_ptr);
215
216 cras_apm_list_destroy(list);
217 cras_apm_list_deinit();
218 delete_tempdir(dir);
219 free(dir);
220 }
221
TEST(ApmList,ApmProcessForwardBuffer)222 TEST(ApmList, ApmProcessForwardBuffer) {
223 struct cras_apm* apm;
224 struct cras_audio_format fmt;
225 struct cras_audio_area* area;
226 struct float_buffer* buf;
227
228 fmt.num_channels = 2;
229 fmt.frame_rate = 48000;
230 fmt.format = SND_PCM_FORMAT_S16_LE;
231 init_channel_layout(&fmt);
232 fmt.channel_layout[CRAS_CH_FL] = 0;
233 fmt.channel_layout[CRAS_CH_FR] = 1;
234
235 cras_apm_list_init("");
236
237 list = cras_apm_list_create(stream_ptr, APM_ECHO_CANCELLATION);
238 EXPECT_NE((void*)NULL, list);
239
240 apm = cras_apm_list_add_apm(list, dev_ptr, &fmt, 1);
241
242 buf = float_buffer_create(500, 2);
243 float_buffer_written(buf, 300);
244 webrtc_apm_process_stream_f_called = 0;
245 cras_apm_list_process(apm, buf, 0);
246 EXPECT_EQ(0, webrtc_apm_process_stream_f_called);
247
248 area = cras_apm_list_get_processed(apm);
249 EXPECT_EQ(0, area->frames);
250
251 float_buffer_reset(buf);
252 float_buffer_written(buf, 200);
253 cras_apm_list_process(apm, buf, 0);
254 area = cras_apm_list_get_processed(apm);
255 EXPECT_EQ(1, webrtc_apm_process_stream_f_called);
256 EXPECT_EQ(480, dsp_util_interleave_frames);
257 EXPECT_EQ(480, area->frames);
258
259 /* Put some processed frames. Another apm_list process will not call
260 * into webrtc_apm because the processed buffer is not yet empty.
261 */
262 cras_apm_list_put_processed(apm, 200);
263 float_buffer_reset(buf);
264 float_buffer_written(buf, 500);
265 cras_apm_list_process(apm, buf, 0);
266 EXPECT_EQ(1, webrtc_apm_process_stream_f_called);
267
268 /* Put another 280 processed frames, so it's now ready for webrtc_apm
269 * to process another chunk of 480 frames (10ms) data.
270 */
271 cras_apm_list_put_processed(apm, 280);
272 cras_apm_list_process(apm, buf, 0);
273 EXPECT_EQ(2, webrtc_apm_process_stream_f_called);
274
275 float_buffer_destroy(&buf);
276 cras_apm_list_destroy(list);
277 cras_apm_list_deinit();
278 }
279
TEST(ApmList,ApmProcessReverseData)280 TEST(ApmList, ApmProcessReverseData) {
281 struct cras_apm* apm;
282 struct cras_audio_format fmt;
283 struct float_buffer* buf;
284 float* const* rp;
285 unsigned int nread;
286 struct cras_iodev fake_iodev;
287
288 fmt.num_channels = 2;
289 fmt.frame_rate = 48000;
290 fmt.format = SND_PCM_FORMAT_S16_LE;
291
292 fake_iodev.direction = CRAS_STREAM_OUTPUT;
293 device_enabled_callback_val = NULL;
294 ext_dsp_module_value = NULL;
295 webrtc_apm_process_reverse_stream_f_called = 0;
296
297 cras_apm_list_init("");
298 EXPECT_NE((void*)NULL, device_enabled_callback_val);
299
300 device_enabled_callback_val(&fake_iodev, NULL);
301 EXPECT_NE((void*)NULL, ext_dsp_module_value);
302 EXPECT_NE((void*)NULL, ext_dsp_module_value->ports);
303
304 buf = float_buffer_create(500, 2);
305 float_buffer_written(buf, 500);
306 nread = 500;
307 rp = float_buffer_read_pointer(buf, 0, &nread);
308
309 for (int i = 0; i < buf->num_channels; i++)
310 ext_dsp_module_value->ports[i] = rp[i];
311
312 ext_dsp_module_value->configure(ext_dsp_module_value, 800, 2, 48000);
313 ext_dsp_module_value->run(ext_dsp_module_value, 500);
314 EXPECT_EQ(0, webrtc_apm_process_reverse_stream_f_called);
315
316 list = cras_apm_list_create(stream_ptr, APM_ECHO_CANCELLATION);
317 EXPECT_NE((void*)NULL, list);
318
319 apm = cras_apm_list_add_apm(list, dev_ptr, &fmt, 1);
320 cras_apm_list_start_apm(list, dev_ptr);
321
322 ext_dsp_module_value->run(ext_dsp_module_value, 250);
323 EXPECT_EQ(0, webrtc_apm_process_reverse_stream_f_called);
324
325 ext_dsp_module_value->run(ext_dsp_module_value, 250);
326 EXPECT_EQ(1, webrtc_apm_process_reverse_stream_f_called);
327
328 float_buffer_destroy(&buf);
329 cras_apm_list_destroy(list);
330 cras_apm_list_deinit();
331 }
332
TEST(ApmList,StreamAddToAlreadyOpenedDev)333 TEST(ApmList, StreamAddToAlreadyOpenedDev) {
334 struct cras_audio_format fmt;
335 struct cras_apm *apm1, *apm2;
336
337 fmt.num_channels = 2;
338 fmt.frame_rate = 48000;
339 fmt.format = SND_PCM_FORMAT_S16_LE;
340
341 cras_apm_list_init("");
342
343 webrtc_apm_create_called = 0;
344 list = cras_apm_list_create(stream_ptr, APM_ECHO_CANCELLATION);
345 EXPECT_NE((void*)NULL, list);
346
347 apm1 = cras_apm_list_add_apm(list, dev_ptr, &fmt, 1);
348 EXPECT_EQ(1, webrtc_apm_create_called);
349 EXPECT_NE((void*)NULL, apm1);
350
351 apm2 = cras_apm_list_add_apm(list, dev_ptr, &fmt, 1);
352 EXPECT_EQ(1, webrtc_apm_create_called);
353 EXPECT_EQ(apm1, apm2);
354
355 cras_apm_list_destroy(list);
356 cras_apm_list_deinit();
357 }
358
359 extern "C" {
cras_iodev_list_set_device_enabled_callback(device_enabled_callback_t enabled_cb,device_disabled_callback_t disabled_cb,void * cb_data)360 int cras_iodev_list_set_device_enabled_callback(
361 device_enabled_callback_t enabled_cb,
362 device_disabled_callback_t disabled_cb,
363 void* cb_data) {
364 device_enabled_callback_val = enabled_cb;
365 return 0;
366 }
cras_iodev_list_get_first_enabled_iodev(enum CRAS_STREAM_DIRECTION direction)367 struct cras_iodev* cras_iodev_list_get_first_enabled_iodev(
368 enum CRAS_STREAM_DIRECTION direction) {
369 return &fake_iodev;
370 }
cras_iodev_set_ext_dsp_module(struct cras_iodev * iodev,struct ext_dsp_module * ext)371 void cras_iodev_set_ext_dsp_module(struct cras_iodev* iodev,
372 struct ext_dsp_module* ext) {
373 ext_dsp_module_value = ext;
374 }
cras_iodev_is_aec_use_case(const struct cras_ionode * node)375 bool cras_iodev_is_aec_use_case(const struct cras_ionode* node) {
376 return cras_iodev_is_aec_use_case_ret;
377 }
cras_audio_area_create(int num_channels)378 struct cras_audio_area* cras_audio_area_create(int num_channels) {
379 return &fake_audio_area;
380 }
381
cras_audio_area_destroy(struct cras_audio_area * area)382 void cras_audio_area_destroy(struct cras_audio_area* area) {}
cras_audio_area_config_channels(struct cras_audio_area * area,const struct cras_audio_format * fmt)383 void cras_audio_area_config_channels(struct cras_audio_area* area,
384 const struct cras_audio_format* fmt) {}
cras_audio_area_config_buf_pointers(struct cras_audio_area * area,const struct cras_audio_format * fmt,uint8_t * base_buffer)385 void cras_audio_area_config_buf_pointers(struct cras_audio_area* area,
386 const struct cras_audio_format* fmt,
387 uint8_t* base_buffer) {}
dsp_util_interleave(float * const * input,int16_t * output,int channels,snd_pcm_format_t format,int frames)388 void dsp_util_interleave(float* const* input,
389 int16_t* output,
390 int channels,
391 snd_pcm_format_t format,
392 int frames) {
393 dsp_util_interleave_frames = frames;
394 }
aec_config_get(const char * device_config_dir)395 struct aec_config* aec_config_get(const char* device_config_dir) {
396 return NULL;
397 }
aec_config_dump(struct aec_config * config)398 void aec_config_dump(struct aec_config* config) {}
apm_config_get(const char * device_config_dir)399 struct apm_config* apm_config_get(const char* device_config_dir) {
400 return NULL;
401 }
apm_config_dump(struct apm_config * config)402 void apm_config_dump(struct apm_config* config) {}
webrtc_apm_create(unsigned int num_channels,unsigned int frame_rate,dictionary * aec_ini,dictionary * apm_ini)403 webrtc_apm webrtc_apm_create(unsigned int num_channels,
404 unsigned int frame_rate,
405 dictionary* aec_ini,
406 dictionary* apm_ini) {
407 webrtc_apm_create_called++;
408 webrtc_apm_create_aec_ini_val = aec_ini;
409 webrtc_apm_create_apm_ini_val = apm_ini;
410 return reinterpret_cast<webrtc_apm>(0x11);
411 }
webrtc_apm_dump_configs(dictionary * aec_ini,dictionary * apm_ini)412 void webrtc_apm_dump_configs(dictionary* aec_ini, dictionary* apm_ini) {}
webrtc_apm_destroy(webrtc_apm apm)413 void webrtc_apm_destroy(webrtc_apm apm) {
414 return;
415 }
webrtc_apm_process_stream_f(webrtc_apm ptr,int num_channels,int rate,float * const * data)416 int webrtc_apm_process_stream_f(webrtc_apm ptr,
417 int num_channels,
418 int rate,
419 float* const* data) {
420 webrtc_apm_process_stream_f_called++;
421 return 0;
422 }
423
webrtc_apm_process_reverse_stream_f(webrtc_apm ptr,int num_channels,int rate,float * const * data)424 int webrtc_apm_process_reverse_stream_f(webrtc_apm ptr,
425 int num_channels,
426 int rate,
427 float* const* data) {
428 webrtc_apm_process_reverse_stream_f_called++;
429 return 0;
430 }
webrtc_apm_aec_dump(webrtc_apm ptr,void ** work_queue,int start,FILE * handle)431 int webrtc_apm_aec_dump(webrtc_apm ptr,
432 void** work_queue,
433 int start,
434 FILE* handle) {
435 return 0;
436 }
437
438 } // extern "C"
439 } // namespace
440
main(int argc,char ** argv)441 int main(int argc, char** argv) {
442 ::testing::InitGoogleTest(&argc, argv);
443 return RUN_ALL_TESTS();
444 }
445