Home | History | Annotate | Download | only in tests
      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