]> git.alsa-project.org Git - alsa-lib.git/commitdiff
Initial working code
authorJaroslav Kysela <perex@perex.cz>
Fri, 7 Dec 2001 17:16:30 +0000 (17:16 +0000)
committerJaroslav Kysela <perex@perex.cz>
Fri, 7 Dec 2001 17:16:30 +0000 (17:16 +0000)
src/pcm/pcm_ladspa.c

index 6be8c349e2d22c6c2bc9a485aa87e2eec7ff8d5a..65a85246034b623a5a9b96faade0997c4d00c9b2 100644 (file)
@@ -39,20 +39,43 @@ typedef enum _snd_pcm_ladspa_policy {
        SND_PCM_LADSPA_POLICY_DUPLICATE         /* duplicate bindings for all channels */
 } snd_pcm_ladspa_policy_t;
 
+typedef struct snd_pcm_ladspa_instance snd_pcm_ladspa_instance_t;
+
 typedef struct {
        /* This field need to be the first */
        snd_pcm_plugin_t plug;
        struct list_head pplugins;
        struct list_head cplugins;
        unsigned int instances_channels;
+       snd_pcm_ladspa_instance_t **finstances;
 } snd_pcm_ladspa_t;
 
+typedef struct {
+       struct list_head list;
+       LADSPA_Handle *handle;
+} snd_pcm_ladspa_subinstance_t;
+
+struct snd_pcm_ladspa_instance {
+       struct list_head list;
+       const LADSPA_Descriptor *desc;
+       LADSPA_Handle *handle;
+       LADSPA_Data *m_data;
+       LADSPA_Data *in_data;
+       LADSPA_Data *out_data;
+       unsigned int depth;
+       unsigned int channel;
+       unsigned int in_port;
+       unsigned int out_port;
+       snd_pcm_ladspa_instance_t *prev;
+       snd_pcm_ladspa_instance_t *next;
+};
+
 typedef struct {
        LADSPA_PortDescriptor pdesc;            /* port description */
        unsigned int port_bindings_size;        /* size of array */
        unsigned int *port_bindings;            /* index = channel number, value = LADSPA port */
        unsigned int controls_size;             /* size of array */
-       LADSPA_Data *controls;                  /* index = LADSPA control port */
+       LADSPA_Data *controls;                  /* index = LADSPA control port index */
 } snd_pcm_ladspa_plugin_io_t;
 
 typedef struct {
@@ -63,9 +86,7 @@ typedef struct {
        const LADSPA_Descriptor *desc;
        snd_pcm_ladspa_plugin_io_t input;
        snd_pcm_ladspa_plugin_io_t output;
-       unsigned int instances_count;
-       LADSPA_Handle *instances;
-       LADSPA_Data **instances_data;
+       struct list_head instances;
 } snd_pcm_ladspa_plugin_t;
 
 static int snd_pcm_ladspa_find_port(unsigned int *res,
@@ -238,48 +259,63 @@ static int snd_pcm_ladspa_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params
 
 static void snd_pcm_ladspa_free_instances(snd_pcm_t *pcm, snd_pcm_ladspa_t *ladspa, int cleanup)
 {
-       struct list_head *list, *pos;
-       unsigned int idx;
+       struct list_head *list, *pos, *pos1, *next1;
        
        if (ladspa->instances_channels == 0)
                return;
        list = pcm->stream == SND_PCM_STREAM_PLAYBACK ? &ladspa->pplugins : &ladspa->cplugins;
        list_for_each(pos, list) {
                snd_pcm_ladspa_plugin_t *plugin = list_entry(pos, snd_pcm_ladspa_plugin_t, list);
-               for (idx = 0; idx < plugin->instances_count; idx++) {
-                       if (plugin->instances[idx] == NULL)
-                               continue;
+               list_for_each_safe(pos1, next1, &plugin->instances) {
+                       snd_pcm_ladspa_instance_t *instance = list_entry(pos1, snd_pcm_ladspa_instance_t, list);
                        if (plugin->desc->deactivate)
-                               plugin->desc->deactivate(plugin->instances[idx]);
+                               plugin->desc->deactivate(instance->handle);
                        if (cleanup) {
                                if (plugin->desc->cleanup)
-                                       plugin->desc->cleanup(plugin->instances[idx]);
+                                       plugin->desc->cleanup(instance->handle);
+                               if (instance->m_data)
+                                       free(instance->m_data);
+                               list_del(&(instance->list));
+                               free(instance);
                        } else {
                                if (plugin->desc->activate)
-                                       plugin->desc->activate(plugin->instances[idx]);
+                                       plugin->desc->activate(instance->handle);
                        }
                }
                if (cleanup) {
-                       free(plugin->instances);
-                       plugin->instances = NULL;
-                       plugin->instances_count = 0;
+                       assert(list_empty(&plugin->instances));
                }
        }
-       if (cleanup)
+       if (cleanup) {
                ladspa->instances_channels = 0;
+               if (ladspa->finstances) {
+                       free(ladspa->finstances);
+                       ladspa->finstances = NULL;
+               }
+       }
 }
 
-static int snd_pcm_ladspa_connect(snd_pcm_ladspa_plugin_t *plugin,
-                                 LADSPA_Handle *instance,
+static int snd_pcm_ladspa_connect(snd_pcm_ladspa_plugin_t *plugin ATTRIBUTE_UNUSED,
+                                 snd_pcm_ladspa_plugin_io_t *io,
+                                 snd_pcm_ladspa_instance_t *instance,
                                  unsigned int channel,
                                  unsigned int port)
 {
+       if (instance->channel == NO_ASSIGN)
+               instance->channel = channel;
+       else if (instance->channel != channel)
+               return -EINVAL;
+       if (io->pdesc == LADSPA_PORT_OUTPUT) {
+               instance->out_port = port;
+       } else {
+               instance->in_port = port;
+       }
        return 0;
 }
 
 static int snd_pcm_ladspa_connect_plugin(snd_pcm_ladspa_plugin_t *plugin,
                                         snd_pcm_ladspa_plugin_io_t *io,
-                                        LADSPA_Handle *instance,
+                                        snd_pcm_ladspa_instance_t *instance,
                                         unsigned int idx)
 {
        unsigned int port;
@@ -288,19 +324,19 @@ static int snd_pcm_ladspa_connect_plugin(snd_pcm_ladspa_plugin_t *plugin,
        assert(plugin->policy == SND_PCM_LADSPA_POLICY_NONE);
        if (io->port_bindings_size > 0) {
                if (idx >= io->port_bindings_size)
-                       return 0;
+                       return instance->channel != NO_ASSIGN ? -EINVAL : 0;
                port = io->port_bindings[idx];
        } else {
                err = snd_pcm_ladspa_find_port(&port, plugin, io->pdesc | LADSPA_PORT_AUDIO, idx);
                if (err < 0)
-                       return 0;
+                       return instance->channel != NO_ASSIGN ? err : 0;
        }
-       return snd_pcm_ladspa_connect(plugin, instance, idx, port);
+       return snd_pcm_ladspa_connect(plugin, io, instance, idx, port);
 }
 
 static int snd_pcm_ladspa_connect_plugin_duplicate(snd_pcm_ladspa_plugin_t *plugin,
                                                   snd_pcm_ladspa_plugin_io_t *io,
-                                                  LADSPA_Handle *instance,
+                                                  snd_pcm_ladspa_instance_t *instance,
                                                   unsigned int idx)
 {
        unsigned int port;
@@ -314,63 +350,165 @@ static int snd_pcm_ladspa_connect_plugin_duplicate(snd_pcm_ladspa_plugin_t *plug
                if (err < 0)
                        return err;
        }
-       return snd_pcm_ladspa_connect(plugin, instance, idx, port);
+       return snd_pcm_ladspa_connect(plugin, io, instance, idx, port);
+}
+
+static int snd_pcm_ladspa_connect_controls(snd_pcm_ladspa_plugin_t *plugin,
+                                          snd_pcm_ladspa_plugin_io_t *io,
+                                          snd_pcm_ladspa_instance_t *instance)
+{
+       unsigned long idx, midx;
+
+       for (idx = midx = 0; idx < plugin->desc->PortCount; idx++)
+               if ((plugin->desc->PortDescriptors[idx] & (io->pdesc | LADSPA_PORT_CONTROL)) == (io->pdesc | LADSPA_PORT_CONTROL)) {
+                       if (io->controls_size > midx) {
+                               plugin->desc->connect_port(instance->handle, idx, &io->controls[midx]);
+                       } else {
+                               return -EINVAL;
+                       }
+                       midx++;
+               }
+       return 0;
 }
 
 static int snd_pcm_ladspa_allocate_instances(snd_pcm_t *pcm, snd_pcm_ladspa_t *ladspa)
 {
        struct list_head *list, *pos;
-       unsigned int idx;
-       LADSPA_Handle *instance;
+       unsigned int depth, idx, count;
+       snd_pcm_ladspa_instance_t *instance;
        int err;
        
        if (ladspa->instances_channels == 0)
                return 0;
        list = pcm->stream == SND_PCM_STREAM_PLAYBACK ? &ladspa->pplugins : &ladspa->cplugins;
+       depth = 0;
        list_for_each(pos, list) {
                snd_pcm_ladspa_plugin_t *plugin = list_entry(pos, snd_pcm_ladspa_plugin_t, list);
-               plugin->instances_count = 1;
+               count = 1;
                if (plugin->policy == SND_PCM_LADSPA_POLICY_DUPLICATE)
-                       plugin->instances_count = pcm->channels;
-               plugin->instances = (LADSPA_Handle *)calloc(plugin->instances_count, sizeof(LADSPA_Handle));
-               if (plugin->instances == NULL)
-                       return -ENOMEM;
-               for (idx = 0; idx < plugin->instances_count; idx++) {
-                       instance = plugin->instances[idx] = plugin->desc->instantiate(plugin->desc, pcm->rate);
-                       if (instance == NULL) {
+                       count = pcm->channels;
+               for (idx = 0; idx < count; idx++) {
+                       instance = (snd_pcm_ladspa_instance_t *)calloc(1, sizeof(snd_pcm_ladspa_instance_t));
+                       if (instance == NULL)
+                               return -ENOMEM;
+                       instance->desc = plugin->desc;
+                       instance->handle = plugin->desc->instantiate(plugin->desc, pcm->rate);
+                       instance->depth = depth;
+                       instance->channel = NO_ASSIGN;
+                       if (instance->handle == NULL) {
                                SNDERR("Unable to create instance of LADSPA plugin '%s'", plugin->desc->Name);
+                               free(instance);
                                return -EINVAL;
                        }
-                       plugin->desc->activate(instance);
+                       list_add_tail(&instance->list, &plugin->instances);
+                       plugin->desc->activate(instance->handle);
                        if (plugin->policy == SND_PCM_LADSPA_POLICY_DUPLICATE) {
                                err = snd_pcm_ladspa_connect_plugin_duplicate(plugin, &plugin->input, instance, idx);
                                if (err < 0) {
-                                       SNDERR("Unable to connect duplicate input port of plugin '%s' channel %u", plugin->desc->Name, idx);
+                                       SNDERR("Unable to connect duplicate input port of plugin '%s' channel %u depth %u", plugin->desc->Name, idx, instance->depth);
                                        return err;
                                }
                                err = snd_pcm_ladspa_connect_plugin_duplicate(plugin, &plugin->output, instance, idx);
                                if (err < 0) {
-                                       SNDERR("Unable to connect duplicate output port of plugin '%s' channel %u", plugin->desc->Name, idx);
+                                       SNDERR("Unable to connect duplicate output port of plugin '%s' channel %u depth %u", plugin->desc->Name, idx, instance->depth);
                                        return err;
                                }
                        }
+                       err = snd_pcm_ladspa_connect_controls(plugin, &plugin->input, instance);
+                       assert(err >= 0);
+                       err = snd_pcm_ladspa_connect_controls(plugin, &plugin->output, instance);
+                       assert(err >= 0);
                }
                if (plugin->policy == SND_PCM_LADSPA_POLICY_NONE) {
-                       instance = plugin->instances[0];
+                       instance = list_entry(plugin->instances.next, snd_pcm_ladspa_instance_t, list);
                        for (idx = 0; idx < pcm->channels; idx++) {
                                err = snd_pcm_ladspa_connect_plugin(plugin, &plugin->input, instance, idx);
                                if (err < 0) {
-                                       SNDERR("Unable to connect input port of plugin '%s' channel %u", plugin->desc->Name, idx);
+                                       SNDERR("Unable to connect input port of plugin '%s' channel %u depth %u", plugin->desc->Name, idx, depth);
                                        return err;
                                }
                                err = snd_pcm_ladspa_connect_plugin(plugin, &plugin->output, instance, idx);
                                if (err < 0) {
-                                       SNDERR("Unable to connect output port of plugin '%s' channel %u", plugin->desc->Name, idx);
+                                       SNDERR("Unable to connect output port of plugin '%s' channel %u depth %u", plugin->desc->Name, idx, depth);
                                        return err;
                                }
                        }
                }
-               
+               depth++;
+       }
+       return 0;
+}
+
+static int snd_pcm_ladspa_allocate_imemory(snd_pcm_ladspa_instance_t *instance, size_t alloc_size)
+{
+       if (instance->prev)
+               instance->in_data = instance->prev->out_data;
+       else
+               instance->in_data = NULL;
+       if (!instance->prev ||
+           (instance->next && LADSPA_IS_INPLACE_BROKEN(instance->desc->Properties))) {
+               instance->m_data = (LADSPA_Data *)malloc(alloc_size * sizeof(LADSPA_Data));
+               if (instance->m_data == NULL)
+                       return -ENOMEM;
+               instance->out_data = instance->m_data;
+       } else {
+               instance->out_data = instance->in_data;
+       }
+       return 0;
+}
+
+static int snd_pcm_ladspa_allocate_memory(snd_pcm_t *pcm, snd_pcm_ladspa_t *ladspa)
+{
+       struct list_head *list, *pos, *pos1;
+       snd_pcm_ladspa_instance_t *instance, *prev;
+       unsigned int channel;
+       int err;
+       
+       if (ladspa->instances_channels == 0)
+               return 0;
+       ladspa->finstances = (snd_pcm_ladspa_instance_t **)calloc(ladspa->instances_channels, sizeof(snd_pcm_ladspa_instance_t *));
+       if (ladspa->finstances == NULL)
+               return -ENOMEM;
+       list = pcm->stream == SND_PCM_STREAM_PLAYBACK ? &ladspa->pplugins : &ladspa->cplugins;
+       for (channel = 0; channel < ladspa->instances_channels; channel++) {
+               prev = NULL;
+               list_for_each(pos, list) {
+                       snd_pcm_ladspa_plugin_t *plugin = list_entry(pos, snd_pcm_ladspa_plugin_t, list);
+                       instance = NULL;
+                       if (list_empty(&plugin->instances))
+                               continue;
+                       list_for_each(pos1, &plugin->instances) {
+                               instance = list_entry(pos1, snd_pcm_ladspa_instance_t, list);
+                               if (instance->channel == NO_ASSIGN) {
+                                       SNDERR("channel %u is not assigned for plugin '%s' depth %u", plugin->desc->Name, instance->channel, instance->depth);
+                                       return -EINVAL;
+                               }
+                               if (instance->channel != channel) {
+                                       instance = NULL;
+                                       continue;
+                               }
+                               break;
+                       }
+                       if (instance == NULL)
+                               continue;
+                       if (ladspa->finstances[channel] == NULL)
+                               ladspa->finstances[channel] = instance;
+                       instance->prev = prev;
+                       if (prev == NULL) {
+                               prev = instance;
+                               continue;               /* nothing to do */
+                       }
+                       prev->next = instance;
+               }
+       }
+       for (channel = 0; channel < ladspa->instances_channels; channel++) {
+               instance = ladspa->finstances[channel];
+               if (instance == NULL)
+                       continue;
+               err = snd_pcm_ladspa_allocate_imemory(instance, pcm->buffer_size);
+               if (err < 0)
+                       return err;
+               break;
        }
        return 0;
 }
@@ -382,11 +520,17 @@ static int snd_pcm_ladspa_init(snd_pcm_t *pcm)
        
        if (pcm->channels != ladspa->instances_channels) {
                snd_pcm_ladspa_free_instances(pcm, ladspa, 1);
+               ladspa->instances_channels = pcm->channels;
                err = snd_pcm_ladspa_allocate_instances(pcm, ladspa);
                if (err < 0) {
                        snd_pcm_ladspa_free_instances(pcm, ladspa, 1);
                        return err;
                }
+               err = snd_pcm_ladspa_allocate_memory(pcm, ladspa);
+               if (err < 0) {
+                       snd_pcm_ladspa_free_instances(pcm, ladspa, 1);
+                       return err;
+               }
        } else {
                snd_pcm_ladspa_free_instances(pcm, ladspa, 0);
        }
@@ -410,13 +554,38 @@ snd_pcm_ladspa_write_areas(snd_pcm_t *pcm,
                           snd_pcm_uframes_t slave_offset,
                           snd_pcm_uframes_t *slave_sizep)
 {
-       // snd_pcm_ladspa_t *ladspa = pcm->private_data;
+       snd_pcm_ladspa_t *ladspa = pcm->private_data;
+       unsigned int channel;
+       
        if (size > *slave_sizep)
                size = *slave_sizep;
-#if 1  // no processing - for testing purposes only
+#if 0  // no processing - for testing purposes only
        snd_pcm_areas_copy(slave_areas, slave_offset,
                           areas, offset,
                           pcm->channels, size, pcm->format);
+#else
+       for (channel = 0; channel < ladspa->instances_channels; channel++) {
+               LADSPA_Data *data = (LADSPA_Data *)((char *)areas[channel].addr + (areas[channel].first / 8));
+               snd_pcm_ladspa_instance_t *instance = ladspa->finstances[channel];
+               data += offset;
+               if (instance == NULL)
+                       snd_pcm_area_copy(&slave_areas[channel], slave_offset,
+                                         &areas[channel], offset,
+                                         size, SND_PCM_FORMAT_FLOAT);
+               while (instance) {
+                       if (instance->in_data != NULL)
+                               data = instance->in_data;
+                       instance->desc->connect_port(instance->handle, instance->in_port, data);
+                       if (instance->next == NULL) {
+                               data = (LADSPA_Data *)((char *)slave_areas[channel].addr + (slave_areas[channel].first / 8));
+                               data += slave_offset;
+                       } else if (instance->out_data != NULL)
+                               data = instance->out_data;
+                       instance->desc->connect_port(instance->handle, instance->out_port, data);
+                       instance->desc->run(instance->handle, size);
+                       instance = instance->next;
+               }
+       }
 #endif
        *slave_sizep = size;
        return size;
@@ -431,13 +600,38 @@ snd_pcm_ladspa_read_areas(snd_pcm_t *pcm,
                          snd_pcm_uframes_t slave_offset,
                          snd_pcm_uframes_t *slave_sizep)
 {
-       // snd_pcm_ladspa_t *ladspa = pcm->private_data;
+       snd_pcm_ladspa_t *ladspa = pcm->private_data;
+       unsigned int channel;
+
        if (size > *slave_sizep)
                size = *slave_sizep;
-#if 1  // no processing - for testing purposes only
+#if 0  // no processing - for testing purposes only
        snd_pcm_areas_copy(areas, offset,
                           slave_areas, slave_offset,
                           pcm->channels, size, pcm->format);
+#else
+       for (channel = 0; channel < ladspa->instances_channels; channel++) {
+               LADSPA_Data *data = (LADSPA_Data *)((char *)slave_areas[channel].addr + (slave_areas[channel].first / 8));
+               snd_pcm_ladspa_instance_t *instance = ladspa->finstances[channel];
+               data += slave_offset;
+               if (instance == NULL)
+                       snd_pcm_area_copy(&slave_areas[channel], slave_offset,
+                                         &areas[channel], offset,
+                                         size, SND_PCM_FORMAT_FLOAT);
+               while (instance) {
+                       if (instance->in_data != NULL)
+                               data = instance->in_data;
+                       instance->desc->connect_port(instance->handle, instance->in_port, data);
+                       if (instance->next == NULL) {
+                               data = (LADSPA_Data *)((char *)areas[channel].addr + (areas[channel].first / 8));
+                               data += offset;
+                       } else if (instance->out_data != NULL)
+                               data = instance->out_data;
+                       instance->desc->connect_port(instance->handle, instance->out_port, data);
+                       instance->desc->run(instance->handle, size);
+                       instance = instance->next;
+               }
+       }
 #endif
        *slave_sizep = size;
        return size;
@@ -479,9 +673,9 @@ static void snd_pcm_ladspa_plugins_dump(struct list_head *list, snd_output_t *ou
 static void snd_pcm_ladspa_dump(snd_pcm_t *pcm, snd_output_t *out)
 {
        snd_pcm_ladspa_t *ladspa = pcm->private_data;
+       snd_output_printf(out, "LADSPA PCM\n");
        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");
                snd_pcm_dump_setup(pcm, out);
@@ -758,7 +952,8 @@ static int snd_pcm_ladspa_parse_ioconfig(snd_pcm_ladspa_plugin_t *lplug,
 
 static int snd_pcm_ladspa_add_plugin(struct list_head *list,
                                     const char *path,
-                                    snd_config_t *plugin)
+                                    snd_config_t *plugin,
+                                    int reverse)
 {
        snd_config_iterator_t i, next;
        const char *label = NULL, *filename = NULL;
@@ -799,6 +994,23 @@ static int snd_pcm_ladspa_add_plugin(struct list_head *list,
                        output = n;
                        continue;
                }
+               if (strcmp(id, "policy") == 0) {
+                       const char *str;
+                       err = snd_config_get_string(n, &str);
+                       if (err < 0) {
+                               SNDERR("policy field must be a string");
+                               return err;
+                       }
+                       if (strcmp(str, "none") == 0)
+                               policy = SND_PCM_LADSPA_POLICY_NONE;
+                       else if (strcmp(str, "duplicate") == 0)
+                               policy = SND_PCM_LADSPA_POLICY_DUPLICATE;
+                       else {
+                               SNDERR("unknown policy definition");
+                               return -EINVAL;
+                       }
+                       continue;
+               }
        }
        if (label == NULL && ladspa_id <= 0) {
                SNDERR("no plugin label or id");
@@ -810,6 +1022,7 @@ static int snd_pcm_ladspa_add_plugin(struct list_head *list,
        lplug->policy = policy;
        lplug->input.pdesc = LADSPA_PORT_INPUT;
        lplug->output.pdesc = LADSPA_PORT_OUTPUT;
+       INIT_LIST_HEAD(&lplug->instances);
        if (filename) {
                err = snd_pcm_ladspa_check_file(lplug, filename, label, ladspa_id);
                if (err < 0) {
@@ -825,7 +1038,11 @@ static int snd_pcm_ladspa_add_plugin(struct list_head *list,
                        return err;
                }
        }
-       list_add(&lplug->list, list);
+       if (!reverse) {
+               list_add_tail(&lplug->list, list);
+       } else {
+               list_add(&lplug->list, list);
+       }
        err = snd_pcm_ladspa_parse_ioconfig(lplug, &lplug->input, input);
        if (err < 0)
                return err;
@@ -837,7 +1054,8 @@ static int snd_pcm_ladspa_add_plugin(struct list_head *list,
 
 static int snd_pcm_ladspa_build_plugins(struct list_head *list,
                                        const char *path,
-                                       snd_config_t *plugins)
+                                       snd_config_t *plugins,
+                                       int reverse)
 {
        snd_config_iterator_t i, next;
        int idx = 0, hit, err;
@@ -863,7 +1081,7 @@ static int snd_pcm_ladspa_build_plugins(struct list_head *list,
                        }
                        if (i == idx) {
                                idx++;
-                               err = snd_pcm_ladspa_add_plugin(list, path, n);
+                               err = snd_pcm_ladspa_add_plugin(list, path, n, reverse);
                                if (err < 0)
                                        return err;
                                hit = 1;
@@ -885,7 +1103,7 @@ int snd_pcm_ladspa_open(snd_pcm_t **pcmp, const char *name,
 {
        snd_pcm_t *pcm;
        snd_pcm_ladspa_t *ladspa;
-       int err;
+       int err, reverse = 0;
 
        assert(pcmp && (ladspa_pplugins || ladspa_cplugins) && slave);
 
@@ -903,15 +1121,21 @@ int snd_pcm_ladspa_open(snd_pcm_t **pcmp, const char *name,
        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;
+       if (slave->stream == SND_PCM_STREAM_PLAYBACK) {
+               err = snd_pcm_ladspa_build_plugins(&ladspa->pplugins, ladspa_path, ladspa_pplugins, reverse);
+               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;
+       if (slave->stream == SND_PCM_STREAM_CAPTURE) {
+               if (ladspa_cplugins == ladspa_pplugins)
+                       reverse = 1;
+               err = snd_pcm_ladspa_build_plugins(&ladspa->cplugins, ladspa_path, ladspa_cplugins, reverse);
+               if (err < 0) {
+                       snd_pcm_ladspa_free(ladspa);
+                       return err;
+               }
        }
 
        err = snd_pcm_new(&pcm, SND_PCM_TYPE_LADSPA, name, slave->stream, slave->mode);
@@ -939,7 +1163,7 @@ int _snd_pcm_ladspa_open(snd_pcm_t **pcmp, const char *name,
        snd_pcm_t *spcm;
        snd_config_t *slave = NULL, *sconf;
        const char *path = NULL;
-       snd_config_t *pplugins = NULL, *cplugins = NULL;
+       snd_config_t *plugins = NULL, *pplugins = NULL, *cplugins = NULL;
        snd_config_for_each(i, next, conf) {
                snd_config_t *n = snd_config_iterator_entry(i);
                const char *id;
@@ -955,6 +1179,10 @@ int _snd_pcm_ladspa_open(snd_pcm_t **pcmp, const char *name,
                        snd_config_get_string(n, &path);
                        continue;
                }
+               if (strcmp(id, "plugins") == 0) {
+                       plugins = n;
+                       continue;
+               }
                if (strcmp(id, "playback_plugins") == 0) {
                        pplugins = n;
                        continue;
@@ -970,6 +1198,14 @@ int _snd_pcm_ladspa_open(snd_pcm_t **pcmp, const char *name,
                SNDERR("slave is not defined");
                return -EINVAL;
        }
+       if (plugins) {
+               if (pplugins || cplugins) {
+                       SNDERR("'plugins' definition cannot be combined with 'playback_plugins' or 'capture_plugins'");
+                       return -EINVAL;
+               }
+               pplugins = plugins;
+               cplugins = plugins;
+       }
        err = snd_pcm_slave_conf(root, slave, &sconf, 0);
        if (err < 0)
                return err;