OSDN Git Service

Added more configuration parsing code, seems working now, but the plugin engine is...
authorJaroslav Kysela <perex@perex.cz>
Mon, 26 Nov 2001 15:28:27 +0000 (15:28 +0000)
committerJaroslav Kysela <perex@perex.cz>
Mon, 26 Nov 2001 15:28:27 +0000 (15:28 +0000)
src/pcm/pcm_ladspa.c

index e01597b..0cfc34d 100644 (file)
@@ -19,7 +19,9 @@
  *
  */
   
-#include <byteswap.h>
+#include <dirent.h>
+#include <dlfcn.h>
+#include <wordexp.h>
 #include "pcm_local.h"
 #include "pcm_plugin.h"
 
 const char *_snd_module_pcm_ladspa = "";
 #endif
 
+#define NO_ASSIGN      0xffffffff
+
+typedef enum _snd_pcm_ladspa_policy {
+       SND_PCM_LADSPA_POLICY_NONE,             /* use bindings only */
+       SND_PCM_LADSPA_POLICY_DUPLICATE         /* duplicate bindings for all channels */
+} snd_pcm_ladspa_policy_t;
+
 typedef struct {
        /* This field need to be the first */
        snd_pcm_plugin_t plug;
+       struct list_head pplugins;
+       struct list_head cplugins;
 } snd_pcm_ladspa_t;
 
-static int snd_pcm_ladspa_hw_refine_cprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
+typedef struct {
+       LADSPA_PortDescriptor pdesc;            /* port description */
+       unsigned int port_bindings_size;        /* size of array */
+       unsigned int *port_bindings;            /* index = channel number, value = LADSPA input port */
+       unsigned int controls_size;             /* size of array */
+       LADSPA_Data *controls;                  /* index = LADSPA control port */
+} snd_pcm_ladspa_plugin_io_t;
+
+typedef struct {
+       struct list_head list;
+       snd_pcm_ladspa_policy_t policy;
+       char *filename;
+       void *dl_handle;
+       const LADSPA_Descriptor *desc;
+       snd_pcm_ladspa_plugin_io_t input;
+       snd_pcm_ladspa_plugin_io_t output;
+} snd_pcm_ladspa_plugin_t;
+
+static void snd_pcm_ladspa_free_plugins(struct list_head *plugins)
+{
+       while (!list_empty(plugins)) {
+               snd_pcm_ladspa_plugin_t *plugin = list_entry(plugins->next, snd_pcm_ladspa_plugin_t, list);
+               if (plugin->dl_handle)
+                       dlclose(plugin->dl_handle);
+               if (plugin->filename)
+                       free(plugin->filename);
+               list_del(&plugin->list);
+               free(plugin);
+       }
+}
+
+static void snd_pcm_ladspa_free(snd_pcm_ladspa_t *ladspa)
+{
+       snd_pcm_ladspa_free_plugins(&ladspa->pplugins);
+       snd_pcm_ladspa_free_plugins(&ladspa->cplugins);
+}
+
+static int snd_pcm_ladspa_close(snd_pcm_t *pcm)
 {
        snd_pcm_ladspa_t *ladspa = pcm->private_data;
+
+       snd_pcm_ladspa_free(ladspa);
+       return snd_pcm_plugin_close(pcm);
+}
+
+static int snd_pcm_ladspa_hw_refine_cprepare(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params)
+{
+       // snd_pcm_ladspa_t *ladspa = pcm->private_data;
        int err;
-       snd_pcm_access_mask_t access_mask = { SND_PCM_ACCBIT_SHM };
+       snd_pcm_access_mask_t access_mask = { SND_PCM_ACCBIT_SHMN };
        err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_ACCESS,
                                         &access_mask);
        if (err < 0)
@@ -54,10 +110,10 @@ static int snd_pcm_ladspa_hw_refine_cprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t
        return 0;
 }
 
-static int snd_pcm_ladspa_hw_refine_sprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *sparams)
+static int snd_pcm_ladspa_hw_refine_sprepare(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *sparams)
 {
-       snd_pcm_ladspa_t *ladspa = pcm->private_data;
-       snd_pcm_access_mask_t saccess_mask = { SND_PCM_ACCBIT_MMAP };
+       // snd_pcm_ladspa_t *ladspa = pcm->private_data;
+       snd_pcm_access_mask_t saccess_mask = { SND_PCM_ACCBIT_MMAPN };
        _snd_pcm_hw_params_any(sparams);
        _snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS,
                                   &saccess_mask);
@@ -114,7 +170,7 @@ static int snd_pcm_ladspa_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
 
 static int snd_pcm_ladspa_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
 {
-       snd_pcm_ladspa_t *ladspa = pcm->private_data;
+       // snd_pcm_ladspa_t *ladspa = pcm->private_data;
        int err = snd_pcm_hw_params_slave(pcm, params,
                                          snd_pcm_ladspa_hw_refine_cchange,
                                          snd_pcm_ladspa_hw_refine_sprepare,
@@ -158,9 +214,44 @@ snd_pcm_ladspa_read_areas(snd_pcm_t *pcm,
        return size;
 }
 
+static void snd_pcm_ladspa_dump_direction(snd_pcm_ladspa_plugin_io_t *io, snd_output_t *out)
+{
+       unsigned int idx;
+
+       if (io->port_bindings_size == 0)
+               goto __control;
+       snd_output_printf(out, "Audio %s port bindings:", io->pdesc == LADSPA_PORT_INPUT ? "input" : "output");
+       for (idx = 0; idx < io->port_bindings_size; idx++) {
+               if (io->port_bindings[idx] != NO_ASSIGN) 
+                       continue;
+               snd_output_printf(out, " %i -> %i", idx, io->port_bindings[idx]);
+       }
+       snd_output_printf(out, "\n");
+      __control:
+       if (io->controls_size == 0)
+               return;
+       snd_output_printf(out, "Control %s port initial values:", io->pdesc == LADSPA_PORT_INPUT ? "input" : "output");
+       for (idx = 0; idx < io->controls_size; idx++)
+               snd_output_printf(out, " %i = %.8f", idx, io->controls[idx]);
+       snd_output_printf(out, "\n");
+}
+
+static void snd_pcm_ladspa_plugins_dump(struct list_head *list, snd_output_t *out)
+{
+       struct list_head *pos;
+       
+       list_for_each(pos, list) {
+               snd_pcm_ladspa_plugin_t *plugin = list_entry(pos, snd_pcm_ladspa_plugin_t, list);
+               snd_pcm_ladspa_dump_direction(&plugin->input, out);
+               snd_pcm_ladspa_dump_direction(&plugin->output, out);
+       }
+}
+
 static void snd_pcm_ladspa_dump(snd_pcm_t *pcm, snd_output_t *out)
 {
        snd_pcm_ladspa_t *ladspa = pcm->private_data;
+       snd_pcm_ladspa_plugins_dump(&ladspa->pplugins, out);
+       snd_pcm_ladspa_plugins_dump(&ladspa->cplugins, out);
        snd_output_printf(out, "LADSPA PCM\n");
        if (pcm->setup) {
                snd_output_printf(out, "Its setup is:\n");
@@ -171,7 +262,7 @@ static void snd_pcm_ladspa_dump(snd_pcm_t *pcm, snd_output_t *out)
 }
 
 snd_pcm_ops_t snd_pcm_ladspa_ops = {
-       close: snd_pcm_plugin_close,
+       close: snd_pcm_ladspa_close,
        info: snd_pcm_plugin_info,
        hw_refine: snd_pcm_ladspa_hw_refine,
        hw_params: snd_pcm_ladspa_hw_params,
@@ -185,26 +276,442 @@ snd_pcm_ops_t snd_pcm_ladspa_ops = {
        munmap: snd_pcm_plugin_munmap,
 };
 
-int snd_pcm_ladspa_open(snd_pcm_t **pcmp, const char *name, snd_pcm_format_t sformat, snd_pcm_t *slave, int close_slave)
+static int snd_pcm_ladspa_check_file(snd_pcm_ladspa_plugin_t * const plugin,
+                                    const char *filename,
+                                    const char *label,
+                                    const unsigned long ladspa_id)
+{
+       void *handle;
+
+       assert(filename);
+       handle = dlopen(filename, RTLD_LAZY);
+       if (handle) {
+               LADSPA_Descriptor_Function fcn = (LADSPA_Descriptor_Function)dlsym(handle, "ladspa_descriptor");
+               if (fcn) {
+                       long idx;
+                       const LADSPA_Descriptor *d;
+                       for (idx = 0; (d = fcn(idx)) != NULL; idx++) {
+                               if (strcmp(label, d->Label))
+                                       continue;
+                               if (ladspa_id > 0 && d->UniqueID != ladspa_id)
+                                       continue;
+                               plugin->filename = strdup(filename);
+                               if (plugin->filename == NULL)
+                                       return -ENOMEM;
+                               plugin->dl_handle = handle;
+                               plugin->desc = d;
+                               return 1;
+                       }
+               }
+               dlclose(handle);
+       }
+       return -ENOENT;
+}
+
+static int snd_pcm_ladspa_check_dir(snd_pcm_ladspa_plugin_t * const plugin,
+                                   const char *path,
+                                   const char *label,
+                                   const unsigned long ladspa_id)
+{
+       DIR *dir;
+       struct dirent * dirent;
+       int len = strlen(path), err;
+       int need_slash;
+       char *filename;
+       
+       if (len < 1)
+               return 0;
+       need_slash = path[len - 1] != '/';
+       
+       dir = opendir(path);
+       if (!dir)
+               return -ENOENT;
+               
+       while (1) {
+               dirent = readdir(dir);
+               if (!dirent) {
+                       closedir(dir);
+                       return 0;
+               }
+               
+               filename = malloc(len + strlen(dirent->d_name) + 1 + need_slash);
+               strcpy(filename, path);
+               if (need_slash)
+                       strcat(filename, "/");
+               strcat(filename, dirent->d_name);
+               err = snd_pcm_ladspa_check_file(plugin, filename, label, ladspa_id);
+               free(filename);
+               if (err < 0 && err != -ENOENT)
+                       return err;
+               if (err > 0)
+                       return 1;
+       }
+       /* never reached */
+       return 0;
+}
+
+static int snd_pcm_ladspa_look_for_plugin(snd_pcm_ladspa_plugin_t * const plugin,
+                                         const char *path,
+                                         const char *label,
+                                         const long ladspa_id)
+{
+       const char *c;
+       size_t l;
+       wordexp_t we;
+       int err;
+       
+       for (c = path; (l = strcspn(c, ": ")) > 0; ) {
+               char name[l + 1];
+               memcpy(name, c, l);
+               name[l] = 0;
+               err = wordexp(name, &we, WRDE_NOCMD);
+               switch (err) {
+               case WRDE_NOSPACE:
+                       return -ENOMEM;
+               case 0:
+                       if (we.we_wordc == 1)
+                               break;
+                       /* Fall through */
+               default:
+                       return -EINVAL;
+               }
+               err = snd_pcm_ladspa_check_dir(plugin, we.we_wordv[0], label, ladspa_id);
+               wordfree(&we);
+               if (err < 0)
+                       return err;
+               if (err > 0)
+                       return 0;
+               c += l;
+               if (!*c)
+                       break;
+               c++;
+       }
+       return -ENOENT;
+}                                        
+
+static int snd_pcm_ladspa_find_port(unsigned int *res,
+                                   snd_pcm_ladspa_plugin_t *lplug,
+                                   LADSPA_PortDescriptor pdesc,
+                                   unsigned int port_idx)
+{
+       unsigned long idx;
+
+       for (idx = 0; idx < lplug->desc->PortCount; idx++)
+               if ((lplug->desc->PortDescriptors[idx] & pdesc) == pdesc) {
+                       if (port_idx == 0) {
+                               *res = idx;
+                               return 0;
+                       }
+                       port_idx--;
+               }
+       return -EINVAL;
+}
+
+static int snd_pcm_ladspa_find_sport(unsigned int *res,
+                                    snd_pcm_ladspa_plugin_t *lplug,
+                                    LADSPA_PortDescriptor pdesc,
+                                    const char *port_name)
+{
+       unsigned long idx;
+
+       for (idx = 0; idx < lplug->desc->PortCount; idx++)
+               if ((lplug->desc->PortDescriptors[idx] & pdesc) == pdesc &&
+                   !strcmp(lplug->desc->PortNames[idx], port_name)) {
+                       *res = idx;
+                       return 0;
+               }
+       return -EINVAL;
+}
+
+static int snd_pcm_ladspa_parse_ioconfig(snd_pcm_ladspa_plugin_t *lplug,
+                                        snd_pcm_ladspa_plugin_io_t *io,
+                                        snd_config_t *conf)
+{
+       snd_config_iterator_t i, next;
+       snd_config_t *bindings = NULL, *controls = NULL;
+       int err;
+       
+       if (conf == NULL)
+               return 0;
+       if (snd_config_get_type(conf) != SND_CONFIG_TYPE_COMPOUND) {
+               SNDERR("input or output definition must be a compound");
+               return -EINVAL;
+       }
+       snd_config_for_each(i, next, conf) {
+               snd_config_t *n = snd_config_iterator_entry(i);
+               const char *id;
+               if (snd_config_get_id(n, &id) < 0)
+                       continue;
+               if (strcmp(id, "bindings") == 0) {
+                       bindings = n;
+                       continue;
+               }
+               if (strcmp(id, "controls") == 0) {
+                       controls = n;
+                       continue;
+               }
+       }
+       if (bindings) {
+               unsigned int count = 0;
+               unsigned int *array;
+               if (snd_config_get_type(bindings) != SND_CONFIG_TYPE_COMPOUND) {
+                       SNDERR("bindings definition must be a compound");
+                       return -EINVAL;
+               }
+               snd_config_for_each(i, next, bindings) {
+                       snd_config_t *n = snd_config_iterator_entry(i);
+                       const char *id;
+                       long channel;
+                       if (snd_config_get_id(n, &id) < 0)
+                               continue;
+                       err = safe_strtol(id, &channel);
+                       if (err < 0 || channel < 0) {
+                               SNDERR("Invalid channel number: %s", id);
+                               return -EINVAL;
+                       }
+                       if (count < (unsigned int)(channel + 1))
+                               count = (unsigned int)(channel + 1);
+               }
+               if (count > 0) {
+                       array = (unsigned int *)calloc(count, sizeof(unsigned int));
+                       memset(array, 0xff, count * sizeof(unsigned int));
+                       io->port_bindings_size = count;
+                       io->port_bindings = array;
+                       snd_config_for_each(i, next, bindings) {
+                               snd_config_t *n = snd_config_iterator_entry(i);
+                               const char *id, *sport;
+                               long channel, port;
+                               if (snd_config_get_id(n, &id) < 0)
+                                       continue;
+                               err = safe_strtol(id, &channel);
+                               if (err < 0 || channel < 0) {
+                                       SNDERR("Invalid channel number: %s", id);
+                                       return -EINVAL;
+                               }
+                               err = snd_config_get_integer(n, &port);
+                               if (err >= 0) {
+                                       err = snd_pcm_ladspa_find_port(&array[channel], lplug, io->pdesc | LADSPA_PORT_AUDIO, port);
+                                       if (err < 0) {
+                                               SNDERR("Unable to find an audio port (%li) for channel %s", port);
+                                               return err;
+                                       }
+                                       continue;
+                               }
+                               err = snd_config_get_string(n, &sport);
+                               if (err < 0) {
+                                       SNDERR("Invalid LADSPA port field type for %s", id);
+                                       return -EINVAL;
+                               }
+                               err = snd_pcm_ladspa_find_sport(&array[channel], lplug, io->pdesc | LADSPA_PORT_AUDIO, sport);
+                               if (err < 0) {
+                                       SNDERR("Unable to find an audio port (%s) for channel %s", sport, id);
+                                       return err;
+                               }
+                       }
+               }
+       }
+       if (controls) {
+               unsigned int count = 0;
+               LADSPA_Data *array;
+               unsigned long idx;
+               if (snd_config_get_type(controls) != SND_CONFIG_TYPE_COMPOUND) {
+                       SNDERR("controls definition must be a compound");
+                       return -EINVAL;
+               }
+               for (idx = 0; idx < lplug->desc->PortCount; idx++)
+                       if ((lplug->desc->PortDescriptors[idx] & (io->pdesc | LADSPA_PORT_CONTROL)) == (io->pdesc | LADSPA_PORT_CONTROL))
+                               count++;
+               array = (LADSPA_Data *)calloc(count, sizeof(LADSPA_Data));
+               io->controls_size = count;
+               io->controls = array;
+               snd_config_for_each(i, next, controls) {
+                       snd_config_t *n = snd_config_iterator_entry(i);
+                       const char *id;
+                       long lval;
+                       unsigned int port;
+                       double dval;
+                       if (snd_config_get_id(n, &id) < 0)
+                               continue;
+                       err = safe_strtol(id, &lval);
+                       if (err >= 0) {
+                               err = snd_pcm_ladspa_find_port(&port, lplug, io->pdesc | LADSPA_PORT_CONTROL, lval);
+                       } else {
+                               err = snd_pcm_ladspa_find_sport(&port, lplug, io->pdesc | LADSPA_PORT_CONTROL, id);
+                       }
+                       if (err < 0) {
+                               SNDERR("Unable to find an control port (%s)", id);
+                               return err;
+                       }
+                       if (snd_config_get_ireal(n, &dval) < 0) {
+                               SNDERR("Control port %s has not an float or integer value", id);
+                               return err;
+                       }
+                       array[port] = (LADSPA_Data)dval;
+               }
+       }
+       return 0;
+}
+
+static int snd_pcm_ladspa_add_plugin(struct list_head *list,
+                                    const char *path,
+                                    snd_config_t *plugin)
+{
+       snd_config_iterator_t i, next;
+       const char *label = NULL, *filename = NULL;
+       long ladspa_id = 0;
+       int err;
+       snd_pcm_ladspa_plugin_t *lplug;
+       snd_pcm_ladspa_policy_t policy = SND_PCM_LADSPA_POLICY_DUPLICATE;
+       snd_config_t *input = NULL, *output = NULL;
+
+       snd_config_for_each(i, next, plugin) {
+               snd_config_t *n = snd_config_iterator_entry(i);
+               const char *id;
+               if (snd_config_get_id(n, &id) < 0)
+                       continue;
+               if (strcmp(id, "label") == 0) {
+                       err = snd_config_get_string(n, &label);
+                       if (err < 0)
+                               return err;
+                       continue;
+               }
+               if (strcmp(id, "id") == 0) {
+                       err = snd_config_get_integer(n, &ladspa_id);
+                       if (err < 0)
+                               return err;
+                       continue;
+               }
+               if (strcmp(id, "filename") == 0) {
+                       err = snd_config_get_string(n, &filename);
+                       if (err < 0)
+                               return err;
+                       continue;
+               }
+               if (strcmp(id, "input") == 0) {
+                       input = n;
+                       continue;
+               }
+               if (strcmp(id, "output") == 0) {
+                       output = n;
+                       continue;
+               }
+       }
+       if (label == NULL && ladspa_id <= 0) {
+               SNDERR("no plugin label or id");
+               return -EINVAL;
+       }
+       lplug = (snd_pcm_ladspa_plugin_t *)calloc(1, sizeof(snd_pcm_ladspa_plugin_t));
+       if (lplug == NULL)
+               return -ENOMEM;
+       lplug->policy = policy;
+       lplug->input.pdesc = LADSPA_PORT_INPUT;
+       lplug->output.pdesc = LADSPA_PORT_OUTPUT;
+       if (filename) {
+               err = snd_pcm_ladspa_check_file(lplug, filename, label, ladspa_id);
+               if (err < 0) {
+                       SNDERR("Unable to load plugin '%s' ID %li, filename '%s'", label, ladspa_id, filename);
+                       free(lplug);
+                       return err;
+               }
+       } else {
+               err = snd_pcm_ladspa_look_for_plugin(lplug, path, label, ladspa_id);
+               if (err < 0) {
+                       SNDERR("Unable to find or load plugin '%s' ID %li, path '%s'", label, ladspa_id, path);
+                       free(lplug);
+                       return err;
+               }
+       }
+       list_add(&lplug->list, list);
+       err = snd_pcm_ladspa_parse_ioconfig(lplug, &lplug->input, input);
+       if (err < 0)
+               return err;
+       err = snd_pcm_ladspa_parse_ioconfig(lplug, &lplug->output, output);
+       if (err < 0)
+               return err;
+       return 0;
+}
+
+static int snd_pcm_ladspa_build_plugins(struct list_head *list,
+                                       const char *path,
+                                       snd_config_t *plugins)
+{
+       snd_config_iterator_t i, next;
+       int idx = 0, hit, err;
+
+       if (plugins == NULL)    /* nothing TODO */
+               return 0;
+       if (snd_config_get_type(plugins) != SND_CONFIG_TYPE_COMPOUND) {
+               SNDERR("plugins must be defined inside a compound");
+               return -EINVAL;
+       }
+       do {
+               hit = 0;
+               snd_config_for_each(i, next, plugins) {
+                       snd_config_t *n = snd_config_iterator_entry(i);
+                       const char *id;
+                       long i;
+                       if (snd_config_get_id(n, &id) < 0)
+                               continue;
+                       err = safe_strtol(id, &i);
+                       if (err < 0) {
+                               SNDERR("id of field %s is not an integer", id);
+                               return err;
+                       }
+                       if (i == idx) {
+                               idx++;
+                               err = snd_pcm_ladspa_add_plugin(list, path, n);
+                               if (err < 0)
+                                       return err;
+                               hit = 1;
+                       }
+               }
+       } while (hit);
+       if (list_empty(list)) {
+               SNDERR("empty plugin list is not accepted");
+               return -EINVAL;
+       }
+       return 0;
+}
+
+int snd_pcm_ladspa_open(snd_pcm_t **pcmp, const char *name,
+                       const char *ladspa_path,
+                       snd_config_t *ladspa_pplugins,
+                       snd_config_t *ladspa_cplugins,
+                       snd_pcm_t *slave, int close_slave)
 {
        snd_pcm_t *pcm;
        snd_pcm_ladspa_t *ladspa;
        int err;
-       assert(pcmp && slave);
-       if (sformat != SND_PCM_FORMAT_FLOAT)
-               return -EINVAL;
+
+       assert(pcmp && (ladspa_pplugins || ladspa_cplugins) && slave);
+
+       if (!ladspa_path && !(ladspa_path = getenv("LADSPA_PATH")))
+               return -ENOENT;
        ladspa = calloc(1, sizeof(snd_pcm_ladspa_t));
-       if (!ladspa) {
+       if (!ladspa)
                return -ENOMEM;
-       }
        ladspa->plug.read = snd_pcm_ladspa_read_areas;
        ladspa->plug.write = snd_pcm_ladspa_write_areas;
        ladspa->plug.slave = slave;
        ladspa->plug.close_slave = close_slave;
 
+       INIT_LIST_HEAD(&ladspa->pplugins);
+       INIT_LIST_HEAD(&ladspa->cplugins);
+
+       err = snd_pcm_ladspa_build_plugins(&ladspa->pplugins, ladspa_path, ladspa_pplugins);
+       if (err < 0) {
+               snd_pcm_ladspa_free(ladspa);
+               return err;
+       }
+       err = snd_pcm_ladspa_build_plugins(&ladspa->cplugins, ladspa_path, ladspa_cplugins);
+       if (err < 0) {
+               snd_pcm_ladspa_free(ladspa);
+               return err;
+       }
+
        err = snd_pcm_new(&pcm, SND_PCM_TYPE_LADSPA, name, slave->stream, slave->mode);
        if (err < 0) {
-               free(ladspa);
+               snd_pcm_ladspa_free(ladspa);
                return err;
        }
        pcm->ops = &snd_pcm_ladspa_ops;
@@ -226,7 +733,8 @@ int _snd_pcm_ladspa_open(snd_pcm_t **pcmp, const char *name,
        int err;
        snd_pcm_t *spcm;
        snd_config_t *slave = NULL, *sconf;
-       snd_pcm_format_t sformat;
+       const char *path = NULL;
+       snd_config_t *pplugins = NULL, *cplugins = NULL;
        snd_config_for_each(i, next, conf) {
                snd_config_t *n = snd_config_iterator_entry(i);
                const char *id;
@@ -238,6 +746,18 @@ int _snd_pcm_ladspa_open(snd_pcm_t **pcmp, const char *name,
                        slave = n;
                        continue;
                }
+               if (strcmp(id, "path") == 0) {
+                       snd_config_get_string(n, &path);
+                       continue;
+               }
+               if (strcmp(id, "playback_plugins") == 0) {
+                       pplugins = n;
+                       continue;
+               }
+               if (strcmp(id, "capture_plugins") == 0) {
+                       cplugins = n;
+                       continue;
+               }
                SNDERR("Unknown field %s", id);
                return -EINVAL;
        }
@@ -245,20 +765,14 @@ int _snd_pcm_ladspa_open(snd_pcm_t **pcmp, const char *name,
                SNDERR("slave is not defined");
                return -EINVAL;
        }
-       err = snd_pcm_slave_conf(root, slave, &sconf, 1,
-                                SND_PCM_HW_PARAM_FORMAT, SCONF_MANDATORY, &sformat);
+       err = snd_pcm_slave_conf(root, slave, &sconf, 0);
        if (err < 0)
                return err;
-       if (sformat != SND_PCM_FORMAT_FLOAT) {
-               snd_config_delete(sconf);
-               SNDERR("invalid slave format, should be float");
-               return -EINVAL;
-       }
        err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode);
        snd_config_delete(sconf);
        if (err < 0)
                return err;
-       err = snd_pcm_ladspa_open(pcmp, name, sformat, spcm, 1);
+       err = snd_pcm_ladspa_open(pcmp, name, path, pplugins, cplugins, spcm, 1);
        if (err < 0)
                snd_pcm_close(spcm);
        return err;