• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "cras_dsp_pipeline.h"
6 
7 #include <gtest/gtest.h>
8 
9 #include "cras_config.h"
10 #include "cras_dsp_module.h"
11 
12 #define MAX_MODULES 10
13 #define MAX_MOCK_PORTS 30
14 #define FILENAME_TEMPLATE "DspIniTest.XXXXXX"
15 
fill_test_data(int16_t * data,size_t size)16 static void fill_test_data(int16_t* data, size_t size) {
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   /* Each time the audio data flow through the mock plugin, the data
23    * will be multiplied by 2 in module->run() below, so if there are n
24    * plugins, the data will be multiplied by (1 << n). */
25   int multiples = (1 << times);
26   for (size_t i = 0; i < size; i++) {
27     EXPECT_EQ(i * multiples, data[i]);
28     if ((int16_t)i * multiples != data[i])
29       return;
30   }
31 }
32 
33 struct data {
34   const char* title;
35   int nr_ports;
36   port_direction port_dir[MAX_MOCK_PORTS];
37   int nr_in_audio;
38   int nr_in_control;
39   int nr_out_audio;
40   int nr_out_control;
41   int in_audio[MAX_MOCK_PORTS];
42   int in_control[MAX_MOCK_PORTS];
43   int out_audio[MAX_MOCK_PORTS];
44   int out_control[MAX_MOCK_PORTS];
45   int properties;
46 
47   int instantiate_called;
48   int sample_rate;
49 
50   int connect_port_called[MAX_MOCK_PORTS];
51   float* data_location[MAX_MOCK_PORTS];
52 
53   int run_called;
54   float input[MAX_MOCK_PORTS];
55   float output[MAX_MOCK_PORTS];
56 
57   int sample_count;
58 
59   int get_delay_called;
60   int deinstantiate_called;
61   int free_module_called;
62   int get_properties_called;
63 };
64 
instantiate(struct dsp_module * module,unsigned long sample_rate)65 static int instantiate(struct dsp_module* module, unsigned long sample_rate) {
66   struct data* data = (struct data*)module->data;
67   data->instantiate_called++;
68   data->sample_rate = sample_rate;
69   return 0;
70 }
71 
connect_port(struct dsp_module * module,unsigned long port,float * data_location)72 static void connect_port(struct dsp_module* module,
73                          unsigned long port,
74                          float* data_location) {
75   struct data* data = (struct data*)module->data;
76   data->connect_port_called[port]++;
77   data->data_location[port] = data_location;
78 }
79 
get_delay(struct dsp_module * module)80 static int get_delay(struct dsp_module* module) {
81   struct data* data = (struct data*)module->data;
82   data->get_delay_called++;
83 
84   /* If the module title is "mN", then use N as the delay. */
85   int delay = 0;
86   sscanf(data->title, "m%d", &delay);
87   return delay;
88 }
89 
run(struct dsp_module * module,unsigned long sample_count)90 static void run(struct dsp_module* module, unsigned long sample_count) {
91   struct data* data = (struct data*)module->data;
92   data->run_called++;
93   data->sample_count = sample_count;
94 
95   for (int i = 0; i < data->nr_ports; i++) {
96     if (data->port_dir[i] == PORT_INPUT)
97       data->input[i] = *data->data_location[i];
98   }
99 
100   /* copy the control port data */
101   for (int i = 0; i < std::min(data->nr_in_control, data->nr_out_control);
102        i++) {
103     int from = data->in_control[i];
104     int to = data->out_control[i];
105     data->data_location[to][0] = data->data_location[from][0];
106   }
107 
108   /* multiply the audio port data by 2 */
109   for (int i = 0; i < std::min(data->nr_in_audio, data->nr_out_audio); i++) {
110     int from = data->in_audio[i];
111     int to = data->out_audio[i];
112     for (unsigned int j = 0; j < sample_count; j++)
113       data->data_location[to][j] = data->data_location[from][j] * 2;
114   }
115 }
116 
deinstantiate(struct dsp_module * module)117 static void deinstantiate(struct dsp_module* module) {
118   struct data* data = (struct data*)module->data;
119   data->deinstantiate_called++;
120 }
121 
free_module(struct dsp_module * module)122 static void free_module(struct dsp_module* module) {
123   struct data* data = (struct data*)module->data;
124   data->free_module_called++;
125 }
126 
really_free_module(struct dsp_module * module)127 static void really_free_module(struct dsp_module* module) {
128   struct data* data = (struct data*)module->data;
129   free(data);
130   free(module);
131 }
132 
get_properties(struct dsp_module * module)133 static int get_properties(struct dsp_module* module) {
134   struct data* data = (struct data*)module->data;
135   data->get_properties_called++;
136   return data->properties;
137 }
dump(struct dsp_module * module,struct dumper * d)138 static void dump(struct dsp_module* module, struct dumper* d) {}
139 
create_mock_module(struct plugin * plugin)140 static struct dsp_module* create_mock_module(struct plugin* plugin) {
141   struct data* data;
142   struct dsp_module* module;
143 
144   data = (struct data*)calloc(1, sizeof(struct data));
145   data->title = plugin->title;
146   data->nr_ports = ARRAY_COUNT(&plugin->ports);
147   for (int i = 0; i < data->nr_ports; i++) {
148     struct port* port = ARRAY_ELEMENT(&plugin->ports, i);
149     data->port_dir[i] = port->direction;
150 
151     if (port->direction == PORT_INPUT) {
152       if (port->type == PORT_AUDIO)
153         data->in_audio[data->nr_in_audio++] = i;
154       else
155         data->in_control[data->nr_in_control++] = i;
156     } else {
157       if (port->type == PORT_AUDIO)
158         data->out_audio[data->nr_out_audio++] = i;
159       else
160         data->out_control[data->nr_out_control++] = i;
161     }
162   }
163   if (strcmp(plugin->label, "inplace_broken") == 0) {
164     data->properties = MODULE_INPLACE_BROKEN;
165   } else {
166     data->properties = 0;
167   }
168 
169   module = (struct dsp_module*)calloc(1, sizeof(struct dsp_module));
170   module->data = data;
171   module->instantiate = &instantiate;
172   module->connect_port = &connect_port;
173   module->get_delay = &get_delay;
174   module->run = &run;
175   module->deinstantiate = &deinstantiate;
176   module->free_module = &free_module;
177   module->get_properties = &get_properties;
178   module->dump = &dump;
179   return module;
180 }
181 
182 static struct dsp_module* modules[MAX_MODULES];
183 static struct dsp_module* cras_dsp_module_set_sink_ext_module_val;
184 static int num_modules;
find_module(const char * name)185 static struct dsp_module* find_module(const char* name) {
186   for (int i = 0; i < num_modules; i++) {
187     struct data* data = (struct data*)modules[i]->data;
188     if (strcmp(name, data->title) == 0)
189       return modules[i];
190   }
191   return NULL;
192 }
193 
194 extern "C" {
cras_dsp_module_load_ladspa(struct plugin * plugin)195 struct dsp_module* cras_dsp_module_load_ladspa(struct plugin* plugin) {
196   return NULL;
197 }
cras_dsp_module_load_builtin(struct plugin * plugin)198 struct dsp_module* cras_dsp_module_load_builtin(struct plugin* plugin) {
199   struct dsp_module* module = create_mock_module(plugin);
200   modules[num_modules++] = module;
201   return module;
202 }
cras_dsp_module_set_sink_ext_module(struct dsp_module * module,struct ext_dsp_module * ext_module)203 void cras_dsp_module_set_sink_ext_module(struct dsp_module* module,
204                                          struct ext_dsp_module* ext_module) {
205   cras_dsp_module_set_sink_ext_module_val = module;
206 }
207 }
208 
209 namespace {
210 
211 class DspPipelineTestSuite : public testing::Test {
212  protected:
SetUp()213   virtual void SetUp() {
214     num_modules = 0;
215     strcpy(filename, FILENAME_TEMPLATE);
216     int fd = mkstemp(filename);
217     fp = fdopen(fd, "w");
218   }
219 
TearDown()220   virtual void TearDown() {
221     CloseFile();
222     unlink(filename);
223   }
224 
CloseFile()225   virtual void CloseFile() {
226     if (fp) {
227       fclose(fp);
228       fp = NULL;
229     }
230   }
231 
232   char filename[sizeof(FILENAME_TEMPLATE) + 1];
233   FILE* fp;
234   struct ext_dsp_module ext_mod;
235 };
236 
TEST_F(DspPipelineTestSuite,Simple)237 TEST_F(DspPipelineTestSuite, Simple) {
238   const char* content =
239       "[M1]\n"
240       "library=builtin\n"
241       "label=source\n"
242       "purpose=capture\n"
243       "output_0={audio}\n"
244       "output_1=<control>\n"
245       "input_2=3.0\n"
246       "[M2]\n"
247       "library=builtin\n"
248       "label=sink\n"
249       "purpose=capture\n"
250       "input_0=<control>\n"
251       "input_1={audio}\n"
252       "\n";
253   fprintf(fp, "%s", content);
254   CloseFile();
255 
256   struct cras_expr_env env = CRAS_EXPR_ENV_INIT;
257   struct ini* ini = cras_dsp_ini_create(filename);
258   ASSERT_TRUE(ini);
259   struct pipeline* p = cras_dsp_pipeline_create(ini, &env, "capture");
260   ASSERT_TRUE(p);
261   ASSERT_EQ(0, cras_dsp_pipeline_load(p));
262 
263   ASSERT_EQ(2, num_modules);
264   struct dsp_module* m1 = find_module("m1");
265   struct dsp_module* m2 = find_module("m2");
266   ASSERT_TRUE(m1);
267   ASSERT_TRUE(m2);
268 
269   ASSERT_EQ(1, cras_dsp_pipeline_get_num_input_channels(p));
270   ASSERT_EQ(0, cras_dsp_pipeline_instantiate(p, 48000));
271 
272   struct data* d1 = (struct data*)m1->data;
273   struct data* d2 = (struct data*)m2->data;
274 
275   /* check m1 */
276   ASSERT_STREQ("m1", d1->title);
277   ASSERT_EQ(3, d1->nr_ports);
278   ASSERT_EQ(PORT_OUTPUT, d1->port_dir[0]);
279   ASSERT_EQ(PORT_OUTPUT, d1->port_dir[1]);
280   ASSERT_EQ(PORT_INPUT, d1->port_dir[2]);
281   ASSERT_EQ(1, d1->instantiate_called);
282   ASSERT_EQ(1, d1->get_delay_called);
283   ASSERT_EQ(48000, d1->sample_rate);
284   ASSERT_EQ(1, d1->connect_port_called[0]);
285   ASSERT_EQ(1, d1->connect_port_called[1]);
286   ASSERT_EQ(1, d1->connect_port_called[2]);
287   ASSERT_TRUE(d1->data_location[0]);
288   ASSERT_TRUE(d1->data_location[1]);
289   ASSERT_TRUE(d1->data_location[2]);
290   ASSERT_EQ(0, d1->run_called);
291   ASSERT_EQ(0, d1->deinstantiate_called);
292   ASSERT_EQ(0, d1->free_module_called);
293   ASSERT_EQ(1, d1->get_properties_called);
294 
295   /* check m2 */
296   ASSERT_STREQ("m2", d2->title);
297   ASSERT_EQ(2, d2->nr_ports);
298   ASSERT_EQ(PORT_INPUT, d2->port_dir[0]);
299   ASSERT_EQ(PORT_INPUT, d2->port_dir[1]);
300   ASSERT_EQ(1, d2->instantiate_called);
301   ASSERT_EQ(1, d2->get_delay_called);
302   ASSERT_EQ(48000, d2->sample_rate);
303   ASSERT_EQ(1, d2->connect_port_called[0]);
304   ASSERT_EQ(1, d2->connect_port_called[1]);
305   ASSERT_TRUE(d2->data_location[0]);
306   ASSERT_TRUE(d2->data_location[1]);
307   ASSERT_EQ(0, d2->run_called);
308   ASSERT_EQ(0, d2->deinstantiate_called);
309   ASSERT_EQ(0, d2->free_module_called);
310   ASSERT_EQ(1, d2->get_properties_called);
311 
312   /* check the buffer is shared */
313   ASSERT_EQ(d1->data_location[0], d2->data_location[1]);
314   ASSERT_EQ(d1->data_location[1], d2->data_location[0]);
315   ASSERT_EQ(1, cras_dsp_pipeline_get_peak_audio_buffers(p));
316 
317   d1->data_location[0][0] = 100;
318   cras_dsp_pipeline_run(p, DSP_BUFFER_SIZE);
319   ASSERT_EQ(1, d1->run_called);
320   ASSERT_EQ(1, d2->run_called);
321   ASSERT_EQ(3, d1->input[2]);
322   ASSERT_EQ(3, d2->input[0]);
323   ASSERT_EQ(100, d2->input[1]);
324 
325   d1->data_location[0][0] = 1000;
326   cras_dsp_pipeline_run(p, DSP_BUFFER_SIZE);
327   ASSERT_EQ(2, d1->run_called);
328   ASSERT_EQ(2, d2->run_called);
329   ASSERT_EQ(3, d1->input[2]);
330   ASSERT_EQ(3, d2->input[0]);
331   ASSERT_EQ(1000, d2->input[1]);
332 
333   /* Expect the sink module "m2" is set. */
334   cras_dsp_pipeline_set_sink_ext_module(p, &ext_mod);
335   struct data* d = (struct data*)cras_dsp_module_set_sink_ext_module_val->data;
336   ASSERT_STREQ("m2", d->title);
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, SND_PCM_FORMAT_S16_LE, 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   /* Expect the sink module "m5" is set. */
481   cras_dsp_pipeline_set_sink_ext_module(p, &ext_mod);
482   struct data* d = (struct data*)cras_dsp_module_set_sink_ext_module_val->data;
483   ASSERT_STREQ("m5", d->title);
484 
485   /* re-instantiate */
486   ASSERT_EQ(1, d5->instantiate_called);
487   ASSERT_EQ(1, d5->get_delay_called);
488   ASSERT_EQ(1 + 3 + 5, cras_dsp_pipeline_get_delay(p));
489 
490   cras_dsp_pipeline_deinstantiate(p);
491   cras_dsp_pipeline_instantiate(p, 44100);
492 
493   ASSERT_EQ(1, d5->deinstantiate_called);
494   ASSERT_EQ(2, d5->instantiate_called);
495   ASSERT_EQ(2, d5->get_delay_called);
496   ASSERT_EQ(1 + 3 + 5, cras_dsp_pipeline_get_delay(p));
497   ASSERT_EQ(0, d5->free_module_called);
498   ASSERT_EQ(44100, d5->sample_rate);
499   ASSERT_EQ(2, d5->connect_port_called[0]);
500   ASSERT_EQ(2, d5->connect_port_called[1]);
501 
502   cras_dsp_pipeline_free(p);
503   cras_dsp_ini_free(ini);
504   cras_expr_env_free(&env);
505 
506   really_free_module(m0);
507   really_free_module(m1);
508   really_free_module(m2);
509   really_free_module(m3);
510   really_free_module(m5);
511 }
512 
513 }  //  namespace
514 
main(int argc,char ** argv)515 int main(int argc, char** argv) {
516   ::testing::InitGoogleTest(&argc, argv);
517   return RUN_ALL_TESTS();
518 }
519