• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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