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 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 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 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 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 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 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 120 static void deinstantiate(struct dsp_module *module) 121 { 122 struct data *data = (struct data *)module->data; 123 data->deinstantiate_called++; 124 } 125 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 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 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 } 145 static void dump(struct dsp_module *module, struct dumper *d) {} 146 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 struct dsp_module *cras_dsp_module_set_sink_ext_module_val; 192 static int num_modules; 193 static struct dsp_module *find_module(const char *name) 194 { 195 for (int i = 0; i < num_modules; i++) { 196 struct data *data = (struct data *)modules[i]->data; 197 if (strcmp(name, data->title) == 0) 198 return modules[i]; 199 } 200 return NULL; 201 } 202 203 extern "C" { 204 struct dsp_module *cras_dsp_module_load_ladspa(struct plugin *plugin) 205 { 206 return NULL; 207 } 208 struct dsp_module *cras_dsp_module_load_builtin(struct plugin *plugin) 209 { 210 struct dsp_module *module = create_mock_module(plugin); 211 modules[num_modules++] = module; 212 return module; 213 } 214 void cras_dsp_module_set_sink_ext_module(struct dsp_module *module, 215 struct ext_dsp_module *ext_module) 216 { 217 cras_dsp_module_set_sink_ext_module_val = module; 218 } 219 } 220 221 namespace { 222 223 class DspPipelineTestSuite : public testing::Test { 224 protected: 225 virtual void SetUp() { 226 num_modules = 0; 227 strcpy(filename, FILENAME_TEMPLATE); 228 int fd = mkstemp(filename); 229 fp = fdopen(fd, "w"); 230 } 231 232 virtual void TearDown() { 233 CloseFile(); 234 unlink(filename); 235 } 236 237 virtual void CloseFile() { 238 if (fp) { 239 fclose(fp); 240 fp = NULL; 241 } 242 } 243 244 char filename[sizeof(FILENAME_TEMPLATE) + 1]; 245 FILE *fp; 246 struct ext_dsp_module ext_mod; 247 }; 248 249 TEST_F(DspPipelineTestSuite, Simple) { 250 const char *content = 251 "[M1]\n" 252 "library=builtin\n" 253 "label=source\n" 254 "purpose=capture\n" 255 "output_0={audio}\n" 256 "output_1=<control>\n" 257 "input_2=3.0\n" 258 "[M2]\n" 259 "library=builtin\n" 260 "label=sink\n" 261 "purpose=capture\n" 262 "input_0=<control>\n" 263 "input_1={audio}\n" 264 "\n"; 265 fprintf(fp, "%s", content); 266 CloseFile(); 267 268 struct cras_expr_env env = CRAS_EXPR_ENV_INIT; 269 struct ini *ini = cras_dsp_ini_create(filename); 270 ASSERT_TRUE(ini); 271 struct pipeline *p = cras_dsp_pipeline_create(ini, &env, "capture"); 272 ASSERT_TRUE(p); 273 ASSERT_EQ(0, cras_dsp_pipeline_load(p)); 274 275 ASSERT_EQ(2, num_modules); 276 struct dsp_module *m1 = find_module("m1"); 277 struct dsp_module *m2 = find_module("m2"); 278 ASSERT_TRUE(m1); 279 ASSERT_TRUE(m2); 280 281 ASSERT_EQ(1, cras_dsp_pipeline_get_num_input_channels(p)); 282 ASSERT_EQ(0, cras_dsp_pipeline_instantiate(p, 48000)); 283 284 struct data *d1 = (struct data *)m1->data; 285 struct data *d2 = (struct data *)m2->data; 286 287 /* check m1 */ 288 ASSERT_STREQ("m1", d1->title); 289 ASSERT_EQ(3, d1->nr_ports); 290 ASSERT_EQ(PORT_OUTPUT, d1->port_dir[0]); 291 ASSERT_EQ(PORT_OUTPUT, d1->port_dir[1]); 292 ASSERT_EQ(PORT_INPUT, d1->port_dir[2]); 293 ASSERT_EQ(1, d1->instantiate_called); 294 ASSERT_EQ(1, d1->get_delay_called); 295 ASSERT_EQ(48000, d1->sample_rate); 296 ASSERT_EQ(1, d1->connect_port_called[0]); 297 ASSERT_EQ(1, d1->connect_port_called[1]); 298 ASSERT_EQ(1, d1->connect_port_called[2]); 299 ASSERT_TRUE(d1->data_location[0]); 300 ASSERT_TRUE(d1->data_location[1]); 301 ASSERT_TRUE(d1->data_location[2]); 302 ASSERT_EQ(0, d1->run_called); 303 ASSERT_EQ(0, d1->deinstantiate_called); 304 ASSERT_EQ(0, d1->free_module_called); 305 ASSERT_EQ(1, d1->get_properties_called); 306 307 /* check m2 */ 308 ASSERT_STREQ("m2", d2->title); 309 ASSERT_EQ(2, d2->nr_ports); 310 ASSERT_EQ(PORT_INPUT, d2->port_dir[0]); 311 ASSERT_EQ(PORT_INPUT, d2->port_dir[1]); 312 ASSERT_EQ(1, d2->instantiate_called); 313 ASSERT_EQ(1, d2->get_delay_called); 314 ASSERT_EQ(48000, d2->sample_rate); 315 ASSERT_EQ(1, d2->connect_port_called[0]); 316 ASSERT_EQ(1, d2->connect_port_called[1]); 317 ASSERT_TRUE(d2->data_location[0]); 318 ASSERT_TRUE(d2->data_location[1]); 319 ASSERT_EQ(0, d2->run_called); 320 ASSERT_EQ(0, d2->deinstantiate_called); 321 ASSERT_EQ(0, d2->free_module_called); 322 ASSERT_EQ(1, d2->get_properties_called); 323 324 /* check the buffer is shared */ 325 ASSERT_EQ(d1->data_location[0], d2->data_location[1]); 326 ASSERT_EQ(d1->data_location[1], d2->data_location[0]); 327 ASSERT_EQ(1, cras_dsp_pipeline_get_peak_audio_buffers(p)); 328 329 d1->data_location[0][0] = 100; 330 cras_dsp_pipeline_run(p, DSP_BUFFER_SIZE); 331 ASSERT_EQ(1, d1->run_called); 332 ASSERT_EQ(1, d2->run_called); 333 ASSERT_EQ(3, d1->input[2]); 334 ASSERT_EQ(3, d2->input[0]); 335 ASSERT_EQ(100, d2->input[1]); 336 337 d1->data_location[0][0] = 1000; 338 cras_dsp_pipeline_run(p, DSP_BUFFER_SIZE); 339 ASSERT_EQ(2, d1->run_called); 340 ASSERT_EQ(2, d2->run_called); 341 ASSERT_EQ(3, d1->input[2]); 342 ASSERT_EQ(3, d2->input[0]); 343 ASSERT_EQ(1000, d2->input[1]); 344 345 /* Expect the sink module "m2" is set. */ 346 cras_dsp_pipeline_set_sink_ext_module(p, &ext_mod); 347 struct data *d = (struct data *) 348 cras_dsp_module_set_sink_ext_module_val->data; 349 ASSERT_STREQ("m2", d->title); 350 351 cras_dsp_pipeline_deinstantiate(p); 352 ASSERT_EQ(1, d1->deinstantiate_called); 353 ASSERT_EQ(1, d2->deinstantiate_called); 354 355 cras_dsp_pipeline_free(p); 356 ASSERT_EQ(1, d1->free_module_called); 357 ASSERT_EQ(1, d2->free_module_called); 358 359 cras_dsp_ini_free(ini); 360 cras_expr_env_free(&env); 361 362 really_free_module(m1); 363 really_free_module(m2); 364 } 365 366 TEST_F(DspPipelineTestSuite, Complex) { 367 /* 368 * / --(b)-- 2 --(c)-- \ 369 * 0 ==(a0, a1)== 1 4 ==(f0,f1)== 5 370 * \ --(d)-- 3 --(e)-- / 371 * 372 * 373 * --(g)-- 6 --(h)-- 374 */ 375 376 const char *content = 377 "[M6]\n" 378 "library=builtin\n" 379 "label=foo\n" 380 "input_0={g}\n" 381 "output_1={h}\n" 382 "[M5]\n" 383 "library=builtin\n" 384 "label=sink\n" 385 "purpose=playback\n" 386 "input_0={f0}\n" 387 "input_1={f1}\n" 388 "[M4]\n" 389 "library=builtin\n" 390 "label=foo\n" 391 "disable=(equal? output_device \"HDMI\")\n" 392 "input_0=3.14\n" 393 "input_1={c}\n" 394 "output_2={f0}\n" 395 "input_3={e}\n" 396 "output_4={f1}\n" 397 "[M3]\n" 398 "library=builtin\n" 399 "label=foo\n" 400 "input_0={d}\n" 401 "output_1={e}\n" 402 "[M2]\n" 403 "library=builtin\n" 404 "label=inplace_broken\n" 405 "input_0={b}\n" 406 "output_1={c}\n" 407 "[M1]\n" 408 "library=builtin\n" 409 "label=foo\n" 410 "disable=(equal? output_device \"USB\")\n" 411 "input_0={a0}\n" 412 "input_1={a1}\n" 413 "output_2={b}\n" 414 "output_3={d}\n" 415 "[M0]\n" 416 "library=builtin\n" 417 "label=source\n" 418 "purpose=playback\n" 419 "output_0={a0}\n" 420 "output_1={a1}\n"; 421 fprintf(fp, "%s", content); 422 CloseFile(); 423 424 struct cras_expr_env env = CRAS_EXPR_ENV_INIT; 425 cras_expr_env_install_builtins(&env); 426 cras_expr_env_set_variable_string(&env, "output_device", "HDMI"); 427 cras_expr_env_set_variable_boolean(&env, "swap_lr_disabled", 1); 428 429 struct ini *ini = cras_dsp_ini_create(filename); 430 ASSERT_TRUE(ini); 431 struct pipeline *p = cras_dsp_pipeline_create(ini, &env, "playback"); 432 ASSERT_TRUE(p); 433 ASSERT_EQ(0, cras_dsp_pipeline_load(p)); 434 435 ASSERT_EQ(5, num_modules); /* one not connected, one disabled */ 436 struct dsp_module *m0 = find_module("m0"); 437 struct dsp_module *m1 = find_module("m1"); 438 struct dsp_module *m2 = find_module("m2"); 439 struct dsp_module *m3 = find_module("m3"); 440 struct dsp_module *m5 = find_module("m5"); 441 442 ASSERT_TRUE(m0); 443 ASSERT_TRUE(m1); 444 ASSERT_TRUE(m2); 445 ASSERT_TRUE(m3); 446 ASSERT_FALSE(find_module("m4")); 447 ASSERT_TRUE(m5); 448 ASSERT_FALSE(find_module("m6")); 449 450 ASSERT_EQ(2, cras_dsp_pipeline_get_num_input_channels(p)); 451 ASSERT_EQ(0, cras_dsp_pipeline_instantiate(p, 48000)); 452 453 struct data *d0 = (struct data *)m0->data; 454 struct data *d1 = (struct data *)m1->data; 455 struct data *d2 = (struct data *)m2->data; 456 struct data *d3 = (struct data *)m3->data; 457 struct data *d5 = (struct data *)m5->data; 458 459 /* 460 * / --(b)-- 2 --(c)-- \ 461 * 0 ==(a0, a1)== 1 4 ==(f0,f1)== 5 462 * \ --(d)-- 3 --(e)-- / 463 * 464 * 465 * --(g)-- 6 --(h)-- 466 */ 467 468 ASSERT_EQ(d0->data_location[0], d1->data_location[0]); 469 ASSERT_EQ(d0->data_location[1], d1->data_location[1]); 470 ASSERT_EQ(d1->data_location[2], d2->data_location[0]); 471 ASSERT_EQ(d1->data_location[3], d3->data_location[0]); 472 ASSERT_NE(d2->data_location[0], d2->data_location[1]); /* inplace-broken */ 473 ASSERT_EQ(d2->data_location[1], d5->data_location[0]); /* m4 is disabled */ 474 ASSERT_EQ(d3->data_location[1], d5->data_location[1]); 475 476 /* need 3 buffers because m2 has inplace-broken flag */ 477 ASSERT_EQ(3, cras_dsp_pipeline_get_peak_audio_buffers(p)); 478 479 int16_t *samples = new int16_t[DSP_BUFFER_SIZE]; 480 fill_test_data(samples, DSP_BUFFER_SIZE); 481 cras_dsp_pipeline_apply(p, (uint8_t*)samples, SND_PCM_FORMAT_S16_LE, 100); 482 /* the data flow through 2 plugins because m4 is disabled. */ 483 verify_processed_data(samples, 100, 2); 484 delete[] samples; 485 486 ASSERT_EQ(1, d1->run_called); 487 ASSERT_EQ(1, d3->run_called); 488 489 /* check m5 */ 490 ASSERT_EQ(1, d5->run_called); 491 ASSERT_EQ(100, d5->sample_count); 492 493 /* Expect the sink module "m5" is set. */ 494 cras_dsp_pipeline_set_sink_ext_module(p, &ext_mod); 495 struct data *d = (struct data *) 496 cras_dsp_module_set_sink_ext_module_val->data; 497 ASSERT_STREQ("m5", d->title); 498 499 /* re-instantiate */ 500 ASSERT_EQ(1, d5->instantiate_called); 501 ASSERT_EQ(1, d5->get_delay_called); 502 ASSERT_EQ(1 + 3 + 5, cras_dsp_pipeline_get_delay(p)); 503 504 cras_dsp_pipeline_deinstantiate(p); 505 cras_dsp_pipeline_instantiate(p, 44100); 506 507 ASSERT_EQ(1, d5->deinstantiate_called); 508 ASSERT_EQ(2, d5->instantiate_called); 509 ASSERT_EQ(2, d5->get_delay_called); 510 ASSERT_EQ(1 + 3 + 5, cras_dsp_pipeline_get_delay(p)); 511 ASSERT_EQ(0, d5->free_module_called); 512 ASSERT_EQ(44100, d5->sample_rate); 513 ASSERT_EQ(2, d5->connect_port_called[0]); 514 ASSERT_EQ(2, d5->connect_port_called[1]); 515 516 cras_dsp_pipeline_free(p); 517 cras_dsp_ini_free(ini); 518 cras_expr_env_free(&env); 519 520 really_free_module(m0); 521 really_free_module(m1); 522 really_free_module(m2); 523 really_free_module(m3); 524 really_free_module(m5); 525 } 526 527 } // namespace 528 529 int main(int argc, char **argv) { 530 ::testing::InitGoogleTest(&argc, argv); 531 return RUN_ALL_TESTS(); 532 } 533