1 // Copyright (c) 2012 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
7 #include "cras_config.h"
8 #include "cras_dsp_module.h"
9 #include "cras_dsp_pipeline.h"
10
11 #define MAX_MODULES 10
12 #define MAX_MOCK_PORTS 30
13 #define FILENAME_TEMPLATE "DspIniTest.XXXXXX"
14
fill_test_data(int16_t * data,size_t size)15 static void fill_test_data(int16_t *data, size_t size)
16 {
17 for (size_t i = 0; i < size; i++)
18 data[i] = i;
19 }
20
verify_processed_data(int16_t * data,size_t size,int times)21 static void verify_processed_data(int16_t *data, size_t size, int times)
22 {
23 /* Each time the audio data flow through the mock plugin, the data
24 * will be multiplied by 2 in module->run() below, so if there are n
25 * plugins, the data will be multiplied by (1 << n). */
26 int multiples = (1 << times);
27 for (size_t i = 0; i < size; i++) {
28 EXPECT_EQ(i * multiples, data[i]);
29 if ((int16_t)i * multiples != data[i])
30 return;
31 }
32 }
33
34 struct data {
35 const char *title;
36 int nr_ports;
37 port_direction port_dir[MAX_MOCK_PORTS];
38 int nr_in_audio;
39 int nr_in_control;
40 int nr_out_audio;
41 int nr_out_control;
42 int in_audio[MAX_MOCK_PORTS];
43 int in_control[MAX_MOCK_PORTS];
44 int out_audio[MAX_MOCK_PORTS];
45 int out_control[MAX_MOCK_PORTS];
46 int properties;
47
48 int instantiate_called;
49 int sample_rate;
50
51 int connect_port_called[MAX_MOCK_PORTS];
52 float *data_location[MAX_MOCK_PORTS];
53
54 int run_called;
55 float input[MAX_MOCK_PORTS];
56 float output[MAX_MOCK_PORTS];
57
58 int sample_count;
59
60 int get_delay_called;
61 int deinstantiate_called;
62 int free_module_called;
63 int get_properties_called;
64 };
65
instantiate(struct dsp_module * module,unsigned long sample_rate)66 static int instantiate(struct dsp_module *module, unsigned long sample_rate)
67 {
68 struct data *data = (struct data *)module->data;
69 data->instantiate_called++;
70 data->sample_rate = sample_rate;
71 return 0;
72 }
73
connect_port(struct dsp_module * module,unsigned long port,float * data_location)74 static void connect_port(struct dsp_module *module, unsigned long port,
75 float *data_location)
76 {
77 struct data *data = (struct data *)module->data;
78 data->connect_port_called[port]++;
79 data->data_location[port] = data_location;
80 }
81
get_delay(struct dsp_module * module)82 static int get_delay(struct dsp_module *module)
83 {
84 struct data *data = (struct data *)module->data;
85 data->get_delay_called++;
86
87 /* If the module title is "mN", then use N as the delay. */
88 int delay = 0;
89 sscanf(data->title, "m%d", &delay);
90 return delay;
91 }
92
run(struct dsp_module * module,unsigned long sample_count)93 static void run(struct dsp_module *module, unsigned long sample_count)
94 {
95 struct data *data = (struct data *)module->data;
96 data->run_called++;
97 data->sample_count = sample_count;
98
99 for (int i = 0; i < data->nr_ports; i++) {
100 if (data->port_dir[i] == PORT_INPUT)
101 data->input[i] = *data->data_location[i];
102 }
103
104 /* copy the control port data */
105 for (int i = 0; i < std::min(data->nr_in_control, data->nr_out_control); i++) {
106 int from = data->in_control[i];
107 int to = data->out_control[i];
108 data->data_location[to][0] = data->data_location[from][0];
109 }
110
111 /* multiply the audio port data by 2 */
112 for (int i = 0; i < std::min(data->nr_in_audio, data->nr_out_audio); i++) {
113 int from = data->in_audio[i];
114 int to = data->out_audio[i];
115 for (unsigned int j = 0; j < sample_count; j++)
116 data->data_location[to][j] = data->data_location[from][j] * 2;
117 }
118 }
119
deinstantiate(struct dsp_module * module)120 static void deinstantiate(struct dsp_module *module)
121 {
122 struct data *data = (struct data *)module->data;
123 data->deinstantiate_called++;
124 }
125
free_module(struct dsp_module * module)126 static void free_module(struct dsp_module *module)
127 {
128 struct data *data = (struct data *)module->data;
129 data->free_module_called++;
130 }
131
really_free_module(struct dsp_module * module)132 static void really_free_module(struct dsp_module *module)
133 {
134 struct data *data = (struct data *)module->data;
135 free(data);
136 free(module);
137 }
138
get_properties(struct dsp_module * module)139 static int get_properties(struct dsp_module *module)
140 {
141 struct data *data = (struct data *)module->data;
142 data->get_properties_called++;
143 return data->properties;
144 }
dump(struct dsp_module * module,struct dumper * d)145 static void dump(struct dsp_module *module, struct dumper *d) {}
146
create_mock_module(struct plugin * plugin)147 static struct dsp_module *create_mock_module(struct plugin *plugin)
148 {
149 struct data *data;
150 struct dsp_module *module;
151
152 data = (struct data *)calloc(1, sizeof(struct data));
153 data->title = plugin->title;
154 data->nr_ports = ARRAY_COUNT(&plugin->ports);
155 for (int i = 0; i < data->nr_ports; i++) {
156 struct port *port = ARRAY_ELEMENT(&plugin->ports, i);
157 data->port_dir[i] = port->direction;
158
159 if (port->direction == PORT_INPUT) {
160 if (port->type == PORT_AUDIO)
161 data->in_audio[data->nr_in_audio++] = i;
162 else
163 data->in_control[data->nr_in_control++] = i;
164 } else {
165 if (port->type == PORT_AUDIO)
166 data->out_audio[data->nr_out_audio++] = i;
167 else
168 data->out_control[data->nr_out_control++] = i;
169 }
170 }
171 if (strcmp(plugin->label, "inplace_broken") == 0) {
172 data->properties = MODULE_INPLACE_BROKEN;
173 } else {
174 data->properties = 0;
175 }
176
177 module = (struct dsp_module*)calloc(1, sizeof(struct dsp_module));
178 module->data = data;
179 module->instantiate = &instantiate;
180 module->connect_port = &connect_port;
181 module->get_delay = &get_delay;
182 module->run = &run;
183 module->deinstantiate = &deinstantiate;
184 module->free_module = &free_module;
185 module->get_properties = &get_properties;
186 module->dump = &dump;
187 return module;
188 }
189
190 static struct dsp_module *modules[MAX_MODULES];
191 static int num_modules;
find_module(const char * name)192 static struct dsp_module *find_module(const char *name)
193 {
194 for (int i = 0; i < num_modules; i++) {
195 struct data *data = (struct data *)modules[i]->data;
196 if (strcmp(name, data->title) == 0)
197 return modules[i];
198 }
199 return NULL;
200 }
201
202 extern "C" {
cras_dsp_module_load_ladspa(struct plugin * plugin)203 struct dsp_module *cras_dsp_module_load_ladspa(struct plugin *plugin)
204 {
205 return NULL;
206 }
cras_dsp_module_load_builtin(struct plugin * plugin)207 struct dsp_module *cras_dsp_module_load_builtin(struct plugin *plugin)
208 {
209 struct dsp_module *module = create_mock_module(plugin);
210 modules[num_modules++] = module;
211 return module;
212 }
213 }
214
215 namespace {
216
217 class DspPipelineTestSuite : public testing::Test {
218 protected:
SetUp()219 virtual void SetUp() {
220 num_modules = 0;
221 strcpy(filename, FILENAME_TEMPLATE);
222 int fd = mkstemp(filename);
223 fp = fdopen(fd, "w");
224 }
225
TearDown()226 virtual void TearDown() {
227 CloseFile();
228 unlink(filename);
229 }
230
CloseFile()231 virtual void CloseFile() {
232 if (fp) {
233 fclose(fp);
234 fp = NULL;
235 }
236 }
237
238 char filename[sizeof(FILENAME_TEMPLATE) + 1];
239 FILE *fp;
240 };
241
TEST_F(DspPipelineTestSuite,Simple)242 TEST_F(DspPipelineTestSuite, Simple) {
243 const char *content =
244 "[M1]\n"
245 "library=builtin\n"
246 "label=source\n"
247 "purpose=capture\n"
248 "output_0={audio}\n"
249 "output_1=<control>\n"
250 "input_2=3.0\n"
251 "[M2]\n"
252 "library=builtin\n"
253 "label=sink\n"
254 "purpose=capture\n"
255 "input_0=<control>\n"
256 "input_1={audio}\n"
257 "\n";
258 fprintf(fp, "%s", content);
259 CloseFile();
260
261 struct cras_expr_env env = CRAS_EXPR_ENV_INIT;
262 struct ini *ini = cras_dsp_ini_create(filename);
263 ASSERT_TRUE(ini);
264 struct pipeline *p = cras_dsp_pipeline_create(ini, &env, "capture");
265 ASSERT_TRUE(p);
266 ASSERT_EQ(0, cras_dsp_pipeline_load(p));
267
268 ASSERT_EQ(2, num_modules);
269 struct dsp_module *m1 = find_module("m1");
270 struct dsp_module *m2 = find_module("m2");
271 ASSERT_TRUE(m1);
272 ASSERT_TRUE(m2);
273
274 ASSERT_EQ(1, cras_dsp_pipeline_get_num_input_channels(p));
275 ASSERT_EQ(0, cras_dsp_pipeline_instantiate(p, 48000));
276
277 struct data *d1 = (struct data *)m1->data;
278 struct data *d2 = (struct data *)m2->data;
279
280 /* check m1 */
281 ASSERT_STREQ("m1", d1->title);
282 ASSERT_EQ(3, d1->nr_ports);
283 ASSERT_EQ(PORT_OUTPUT, d1->port_dir[0]);
284 ASSERT_EQ(PORT_OUTPUT, d1->port_dir[1]);
285 ASSERT_EQ(PORT_INPUT, d1->port_dir[2]);
286 ASSERT_EQ(1, d1->instantiate_called);
287 ASSERT_EQ(1, d1->get_delay_called);
288 ASSERT_EQ(48000, d1->sample_rate);
289 ASSERT_EQ(1, d1->connect_port_called[0]);
290 ASSERT_EQ(1, d1->connect_port_called[1]);
291 ASSERT_EQ(1, d1->connect_port_called[2]);
292 ASSERT_TRUE(d1->data_location[0]);
293 ASSERT_TRUE(d1->data_location[1]);
294 ASSERT_TRUE(d1->data_location[2]);
295 ASSERT_EQ(0, d1->run_called);
296 ASSERT_EQ(0, d1->deinstantiate_called);
297 ASSERT_EQ(0, d1->free_module_called);
298 ASSERT_EQ(1, d1->get_properties_called);
299
300 /* check m2 */
301 ASSERT_STREQ("m2", d2->title);
302 ASSERT_EQ(2, d2->nr_ports);
303 ASSERT_EQ(PORT_INPUT, d2->port_dir[0]);
304 ASSERT_EQ(PORT_INPUT, d2->port_dir[1]);
305 ASSERT_EQ(1, d2->instantiate_called);
306 ASSERT_EQ(1, d2->get_delay_called);
307 ASSERT_EQ(48000, d2->sample_rate);
308 ASSERT_EQ(1, d2->connect_port_called[0]);
309 ASSERT_EQ(1, d2->connect_port_called[1]);
310 ASSERT_TRUE(d2->data_location[0]);
311 ASSERT_TRUE(d2->data_location[1]);
312 ASSERT_EQ(0, d2->run_called);
313 ASSERT_EQ(0, d2->deinstantiate_called);
314 ASSERT_EQ(0, d2->free_module_called);
315 ASSERT_EQ(1, d2->get_properties_called);
316
317 /* check the buffer is shared */
318 ASSERT_EQ(d1->data_location[0], d2->data_location[1]);
319 ASSERT_EQ(d1->data_location[1], d2->data_location[0]);
320 ASSERT_EQ(1, cras_dsp_pipeline_get_peak_audio_buffers(p));
321
322 d1->data_location[0][0] = 100;
323 cras_dsp_pipeline_run(p, DSP_BUFFER_SIZE);
324 ASSERT_EQ(1, d1->run_called);
325 ASSERT_EQ(1, d2->run_called);
326 ASSERT_EQ(3, d1->input[2]);
327 ASSERT_EQ(3, d2->input[0]);
328 ASSERT_EQ(100, d2->input[1]);
329
330 d1->data_location[0][0] = 1000;
331 cras_dsp_pipeline_run(p, DSP_BUFFER_SIZE);
332 ASSERT_EQ(2, d1->run_called);
333 ASSERT_EQ(2, d2->run_called);
334 ASSERT_EQ(3, d1->input[2]);
335 ASSERT_EQ(3, d2->input[0]);
336 ASSERT_EQ(1000, d2->input[1]);
337
338 cras_dsp_pipeline_deinstantiate(p);
339 ASSERT_EQ(1, d1->deinstantiate_called);
340 ASSERT_EQ(1, d2->deinstantiate_called);
341
342 cras_dsp_pipeline_free(p);
343 ASSERT_EQ(1, d1->free_module_called);
344 ASSERT_EQ(1, d2->free_module_called);
345
346 cras_dsp_ini_free(ini);
347 cras_expr_env_free(&env);
348
349 really_free_module(m1);
350 really_free_module(m2);
351 }
352
TEST_F(DspPipelineTestSuite,Complex)353 TEST_F(DspPipelineTestSuite, Complex) {
354 /*
355 * / --(b)-- 2 --(c)-- \
356 * 0 ==(a0, a1)== 1 4 ==(f0,f1)== 5
357 * \ --(d)-- 3 --(e)-- /
358 *
359 *
360 * --(g)-- 6 --(h)--
361 */
362
363 const char *content =
364 "[M6]\n"
365 "library=builtin\n"
366 "label=foo\n"
367 "input_0={g}\n"
368 "output_1={h}\n"
369 "[M5]\n"
370 "library=builtin\n"
371 "label=sink\n"
372 "purpose=playback\n"
373 "input_0={f0}\n"
374 "input_1={f1}\n"
375 "[M4]\n"
376 "library=builtin\n"
377 "label=foo\n"
378 "disable=(equal? output_device \"HDMI\")\n"
379 "input_0=3.14\n"
380 "input_1={c}\n"
381 "output_2={f0}\n"
382 "input_3={e}\n"
383 "output_4={f1}\n"
384 "[M3]\n"
385 "library=builtin\n"
386 "label=foo\n"
387 "input_0={d}\n"
388 "output_1={e}\n"
389 "[M2]\n"
390 "library=builtin\n"
391 "label=inplace_broken\n"
392 "input_0={b}\n"
393 "output_1={c}\n"
394 "[M1]\n"
395 "library=builtin\n"
396 "label=foo\n"
397 "disable=(equal? output_device \"USB\")\n"
398 "input_0={a0}\n"
399 "input_1={a1}\n"
400 "output_2={b}\n"
401 "output_3={d}\n"
402 "[M0]\n"
403 "library=builtin\n"
404 "label=source\n"
405 "purpose=playback\n"
406 "output_0={a0}\n"
407 "output_1={a1}\n";
408 fprintf(fp, "%s", content);
409 CloseFile();
410
411 struct cras_expr_env env = CRAS_EXPR_ENV_INIT;
412 cras_expr_env_install_builtins(&env);
413 cras_expr_env_set_variable_string(&env, "output_device", "HDMI");
414 cras_expr_env_set_variable_boolean(&env, "swap_lr_disabled", 1);
415
416 struct ini *ini = cras_dsp_ini_create(filename);
417 ASSERT_TRUE(ini);
418 struct pipeline *p = cras_dsp_pipeline_create(ini, &env, "playback");
419 ASSERT_TRUE(p);
420 ASSERT_EQ(0, cras_dsp_pipeline_load(p));
421
422 ASSERT_EQ(5, num_modules); /* one not connected, one disabled */
423 struct dsp_module *m0 = find_module("m0");
424 struct dsp_module *m1 = find_module("m1");
425 struct dsp_module *m2 = find_module("m2");
426 struct dsp_module *m3 = find_module("m3");
427 struct dsp_module *m5 = find_module("m5");
428
429 ASSERT_TRUE(m0);
430 ASSERT_TRUE(m1);
431 ASSERT_TRUE(m2);
432 ASSERT_TRUE(m3);
433 ASSERT_FALSE(find_module("m4"));
434 ASSERT_TRUE(m5);
435 ASSERT_FALSE(find_module("m6"));
436
437 ASSERT_EQ(2, cras_dsp_pipeline_get_num_input_channels(p));
438 ASSERT_EQ(0, cras_dsp_pipeline_instantiate(p, 48000));
439
440 struct data *d0 = (struct data *)m0->data;
441 struct data *d1 = (struct data *)m1->data;
442 struct data *d2 = (struct data *)m2->data;
443 struct data *d3 = (struct data *)m3->data;
444 struct data *d5 = (struct data *)m5->data;
445
446 /*
447 * / --(b)-- 2 --(c)-- \
448 * 0 ==(a0, a1)== 1 4 ==(f0,f1)== 5
449 * \ --(d)-- 3 --(e)-- /
450 *
451 *
452 * --(g)-- 6 --(h)--
453 */
454
455 ASSERT_EQ(d0->data_location[0], d1->data_location[0]);
456 ASSERT_EQ(d0->data_location[1], d1->data_location[1]);
457 ASSERT_EQ(d1->data_location[2], d2->data_location[0]);
458 ASSERT_EQ(d1->data_location[3], d3->data_location[0]);
459 ASSERT_NE(d2->data_location[0], d2->data_location[1]); /* inplace-broken */
460 ASSERT_EQ(d2->data_location[1], d5->data_location[0]); /* m4 is disabled */
461 ASSERT_EQ(d3->data_location[1], d5->data_location[1]);
462
463 /* need 3 buffers because m2 has inplace-broken flag */
464 ASSERT_EQ(3, cras_dsp_pipeline_get_peak_audio_buffers(p));
465
466 int16_t *samples = new int16_t[DSP_BUFFER_SIZE];
467 fill_test_data(samples, DSP_BUFFER_SIZE);
468 cras_dsp_pipeline_apply(p, (uint8_t*)samples, 100);
469 /* the data flow through 2 plugins because m4 is disabled. */
470 verify_processed_data(samples, 100, 2);
471 delete[] samples;
472
473 ASSERT_EQ(1, d1->run_called);
474 ASSERT_EQ(1, d3->run_called);
475
476 /* check m5 */
477 ASSERT_EQ(1, d5->run_called);
478 ASSERT_EQ(100, d5->sample_count);
479
480 /* re-instantiate */
481 ASSERT_EQ(1, d5->instantiate_called);
482 ASSERT_EQ(1, d5->get_delay_called);
483 ASSERT_EQ(1 + 3 + 5, cras_dsp_pipeline_get_delay(p));
484
485 cras_dsp_pipeline_deinstantiate(p);
486 cras_dsp_pipeline_instantiate(p, 44100);
487
488 ASSERT_EQ(1, d5->deinstantiate_called);
489 ASSERT_EQ(2, d5->instantiate_called);
490 ASSERT_EQ(2, d5->get_delay_called);
491 ASSERT_EQ(1 + 3 + 5, cras_dsp_pipeline_get_delay(p));
492 ASSERT_EQ(0, d5->free_module_called);
493 ASSERT_EQ(44100, d5->sample_rate);
494 ASSERT_EQ(2, d5->connect_port_called[0]);
495 ASSERT_EQ(2, d5->connect_port_called[1]);
496
497 cras_dsp_pipeline_free(p);
498 cras_dsp_ini_free(ini);
499 cras_expr_env_free(&env);
500
501 really_free_module(m0);
502 really_free_module(m1);
503 really_free_module(m2);
504 really_free_module(m3);
505 really_free_module(m5);
506 }
507
508 } // namespace
509
main(int argc,char ** argv)510 int main(int argc, char **argv) {
511 ::testing::InitGoogleTest(&argc, argv);
512 return RUN_ALL_TESTS();
513 }
514