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 struct dsp_module *cras_dsp_module_set_sink_ext_module_val;
192 static int num_modules;
find_module(const char * name)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" {
cras_dsp_module_load_ladspa(struct plugin * plugin)204 struct dsp_module *cras_dsp_module_load_ladspa(struct plugin *plugin)
205 {
206   return NULL;
207 }
cras_dsp_module_load_builtin(struct plugin * plugin)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 }
cras_dsp_module_set_sink_ext_module(struct dsp_module * module,struct ext_dsp_module * ext_module)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:
SetUp()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 
TearDown()232   virtual void TearDown() {
233     CloseFile();
234     unlink(filename);
235   }
236 
CloseFile()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 
TEST_F(DspPipelineTestSuite,Simple)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 
TEST_F(DspPipelineTestSuite,Complex)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 
main(int argc,char ** argv)529 int main(int argc, char **argv) {
530   ::testing::InitGoogleTest(&argc, argv);
531   return RUN_ALL_TESTS();
532 }
533