]> git.alsa-project.org Git - alsa-lib.git/commitdiff
big ALSA LADSPA plugin rewrite for multiple channel LADSPA plugins
authorJaroslav Kysela <perex@perex.cz>
Tue, 13 Dec 2005 14:08:58 +0000 (14:08 +0000)
committerJaroslav Kysela <perex@perex.cz>
Tue, 13 Dec 2005 14:08:58 +0000 (14:08 +0000)
This is major rewrite of the LADSPA plugin to satisfy requirements for
the LADSPA plugins with multiple audio inputs and / or outputs.

src/pcm/ladspa.h
src/pcm/pcm_ladspa.c

index 5e2fa98d1b2d549d88420f54e300c293f424bb86..5c30a8a4b5c081c820b603ce2a6f071d5aefb274 100644 (file)
@@ -1,7 +1,7 @@
 /* ladspa.h
 
-   Linux Audio Developer's Simple Plugin API Version 1.0[LGPL].
-   Copyright (C) 2000-2001 Richard W.E. Furse, Paul Barton-Davis,
+   Linux Audio Developer's Simple Plugin API Version 1.1[LGPL].
+   Copyright (C) 2000-2002 Richard W.E. Furse, Paul Barton-Davis,
    Stefan Westerfeld.
    
    This library is free software; you can redistribute it and/or
 #ifndef LADSPA_INCLUDED
 #define LADSPA_INCLUDED
 
+#define LADSPA_VERSION "1.1"
+#define LADSPA_VERSION_MAJOR 1
+#define LADSPA_VERSION_MINOR 1
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -72,7 +76,10 @@ extern "C" {
 /* Fundamental data type passed in and out of plugin. This data type
    is used to communicate audio samples and control values. It is
    assumed that the plugin will work sensibly given any numeric input
-   value although it may have a preferred range (see hints below). */
+   value although it may have a preferred range (see hints below). 
+
+   For audio it is generally assumed that 1.0f is the `0dB' reference
+   amplitude and is a `normal' signal level. */
 
 typedef float LADSPA_Data;
 
@@ -198,7 +205,7 @@ typedef int LADSPA_PortRangeHintDescriptor;
    bound of the valid range. If LADSPA_HINT_SAMPLE_RATE is also
    specified then the value of LowerBound should be multiplied by the
    sample rate. */
-#define LADSPA_HINT_BOUNDED_BELOW 0x1
+#define LADSPA_HINT_BOUNDED_BELOW   0x1
 
 /* Hint LADSPA_HINT_BOUNDED_ABOVE indicates that the UpperBound field
    of the LADSPA_PortRangeHint should be considered meaningful. The
@@ -206,14 +213,15 @@ typedef int LADSPA_PortRangeHintDescriptor;
    bound of the valid range. If LADSPA_HINT_SAMPLE_RATE is also
    specified then the value of UpperBound should be multiplied by the
    sample rate. */
-#define LADSPA_HINT_BOUNDED_ABOVE 0x2
+#define LADSPA_HINT_BOUNDED_ABOVE   0x2
 
 /* Hint LADSPA_HINT_TOGGLED indicates that the data item should be
    considered a Boolean toggle. Data less than or equal to zero should
    be considered `off' or `false,' and data above zero should be
    considered `on' or `true.' LADSPA_HINT_TOGGLED may not be used in
-   conjunction with any other hint. */
-#define LADSPA_HINT_TOGGLED       0x4
+   conjunction with any other hint except LADSPA_HINT_DEFAULT_0 or
+   LADSPA_HINT_DEFAULT_1. */
+#define LADSPA_HINT_TOGGLED         0x4
 
 /* Hint LADSPA_HINT_SAMPLE_RATE indicates that any bounds specified
    should be interpreted as multiples of the sample rate. For
@@ -221,12 +229,12 @@ typedef int LADSPA_PortRangeHintDescriptor;
    the sample rate) could be requested by this hint in conjunction
    with LowerBound = 0 and UpperBound = 0.5. Hosts that support bounds
    at all must support this hint to retain meaning. */
-#define LADSPA_HINT_SAMPLE_RATE   0x8
+#define LADSPA_HINT_SAMPLE_RATE     0x8
 
 /* Hint LADSPA_HINT_LOGARITHMIC indicates that it is likely that the
    user will find it more intuitive to view values using a logarithmic
    scale. This is particularly useful for frequencies and gains. */
-#define LADSPA_HINT_LOGARITHMIC   0x10
+#define LADSPA_HINT_LOGARITHMIC     0x10
 
 /* Hint LADSPA_HINT_INTEGER indicates that a user interface would
    probably wish to provide a stepped control taking only integer
@@ -234,14 +242,97 @@ typedef int LADSPA_PortRangeHintDescriptor;
    integer range required to avoid floating point rounding errors. For
    instance, the integer set {0,1,2,3} might be described as [-0.1,
    3.1]. */
-#define LADSPA_HINT_INTEGER       0x20
-
-#define LADSPA_IS_HINT_BOUNDED_BELOW(x) ((x) & LADSPA_HINT_BOUNDED_BELOW)
-#define LADSPA_IS_HINT_BOUNDED_ABOVE(x) ((x) & LADSPA_HINT_BOUNDED_ABOVE)
-#define LADSPA_IS_HINT_TOGGLED(x)       ((x) & LADSPA_HINT_TOGGLED)
-#define LADSPA_IS_HINT_SAMPLE_RATE(x)   ((x) & LADSPA_HINT_SAMPLE_RATE)
-#define LADSPA_IS_HINT_LOGARITHMIC(x)   ((x) & LADSPA_HINT_LOGARITHMIC)
-#define LADSPA_IS_HINT_INTEGER(x)       ((x) & LADSPA_HINT_INTEGER)
+#define LADSPA_HINT_INTEGER         0x20
+
+/* The various LADSPA_HINT_HAS_DEFAULT_* hints indicate a `normal'
+   value for the port that is sensible as a default. For instance,
+   this value is suitable for use as an initial value in a user
+   interface or as a value the host might assign to a control port
+   when the user has not provided one. Defaults are encoded using a
+   mask so only one default may be specified for a port. Some of the
+   hints make use of lower and upper bounds, in which case the
+   relevant bound or bounds must be available and
+   LADSPA_HINT_SAMPLE_RATE must be applied as usual. The resulting
+   default must be rounded if LADSPA_HINT_INTEGER is present. Default
+   values were introduced in LADSPA v1.1. */
+#define LADSPA_HINT_DEFAULT_MASK    0x3C0
+
+/* This default values indicates that no default is provided. */
+#define LADSPA_HINT_DEFAULT_NONE    0x0
+
+/* This default hint indicates that the suggested lower bound for the
+   port should be used. */
+#define LADSPA_HINT_DEFAULT_MINIMUM 0x40
+
+/* This default hint indicates that a low value between the suggested
+   lower and upper bounds should be chosen. For ports with
+   LADSPA_HINT_LOGARITHMIC, this should be exp(log(lower) * 0.75 +
+   log(upper) * 0.25). Otherwise, this should be (lower * 0.75 + upper
+   * 0.25). */
+#define LADSPA_HINT_DEFAULT_LOW     0x80
+
+/* This default hint indicates that a middle value between the
+   suggested lower and upper bounds should be chosen. For ports with
+   LADSPA_HINT_LOGARITHMIC, this should be exp(log(lower) * 0.5 +
+   log(upper) * 0.5). Otherwise, this should be (lower * 0.5 + upper *
+   0.5). */
+#define LADSPA_HINT_DEFAULT_MIDDLE  0xC0
+
+/* This default hint indicates that a high value between the suggested
+   lower and upper bounds should be chosen. For ports with
+   LADSPA_HINT_LOGARITHMIC, this should be exp(log(lower) * 0.25 +
+   log(upper) * 0.75). Otherwise, this should be (lower * 0.25 + upper
+   * 0.75). */
+#define LADSPA_HINT_DEFAULT_HIGH    0x100
+
+/* This default hint indicates that the suggested upper bound for the
+   port should be used. */
+#define LADSPA_HINT_DEFAULT_MAXIMUM 0x140
+
+/* This default hint indicates that the number 0 should be used. Note
+   that this default may be used in conjunction with
+   LADSPA_HINT_TOGGLED. */
+#define LADSPA_HINT_DEFAULT_0       0x200
+
+/* This default hint indicates that the number 1 should be used. Note
+   that this default may be used in conjunction with
+   LADSPA_HINT_TOGGLED. */
+#define LADSPA_HINT_DEFAULT_1       0x240
+
+/* This default hint indicates that the number 100 should be used. */
+#define LADSPA_HINT_DEFAULT_100     0x280
+
+/* This default hint indicates that the Hz frequency of `concert A'
+   should be used. This will be 440 unless the host uses an unusual
+   tuning convention, in which case it may be within a few Hz. */
+#define LADSPA_HINT_DEFAULT_440     0x2C0
+
+#define LADSPA_IS_HINT_BOUNDED_BELOW(x)   ((x) & LADSPA_HINT_BOUNDED_BELOW)
+#define LADSPA_IS_HINT_BOUNDED_ABOVE(x)   ((x) & LADSPA_HINT_BOUNDED_ABOVE)
+#define LADSPA_IS_HINT_TOGGLED(x)         ((x) & LADSPA_HINT_TOGGLED)
+#define LADSPA_IS_HINT_SAMPLE_RATE(x)     ((x) & LADSPA_HINT_SAMPLE_RATE)
+#define LADSPA_IS_HINT_LOGARITHMIC(x)     ((x) & LADSPA_HINT_LOGARITHMIC)
+#define LADSPA_IS_HINT_INTEGER(x)         ((x) & LADSPA_HINT_INTEGER)
+
+#define LADSPA_IS_HINT_HAS_DEFAULT(x)     ((x) & LADSPA_HINT_DEFAULT_MASK)
+#define LADSPA_IS_HINT_DEFAULT_MINIMUM(x) (((x) & LADSPA_HINT_DEFAULT_MASK)   \
+                                           == LADSPA_HINT_DEFAULT_MINIMUM)
+#define LADSPA_IS_HINT_DEFAULT_LOW(x)     (((x) & LADSPA_HINT_DEFAULT_MASK)   \
+                                           == LADSPA_HINT_DEFAULT_LOW)
+#define LADSPA_IS_HINT_DEFAULT_MIDDLE(x)  (((x) & LADSPA_HINT_DEFAULT_MASK)   \
+                                           == LADSPA_HINT_DEFAULT_MIDDLE)
+#define LADSPA_IS_HINT_DEFAULT_HIGH(x)    (((x) & LADSPA_HINT_DEFAULT_MASK)   \
+                                           == LADSPA_HINT_DEFAULT_HIGH)
+#define LADSPA_IS_HINT_DEFAULT_MAXIMUM(x) (((x) & LADSPA_HINT_DEFAULT_MASK)   \
+                                           == LADSPA_HINT_DEFAULT_MAXIMUM)
+#define LADSPA_IS_HINT_DEFAULT_0(x)       (((x) & LADSPA_HINT_DEFAULT_MASK)   \
+                                           == LADSPA_HINT_DEFAULT_0)
+#define LADSPA_IS_HINT_DEFAULT_1(x)       (((x) & LADSPA_HINT_DEFAULT_MASK)   \
+                                           == LADSPA_HINT_DEFAULT_1)
+#define LADSPA_IS_HINT_DEFAULT_100(x)     (((x) & LADSPA_HINT_DEFAULT_MASK)   \
+                                           == LADSPA_HINT_DEFAULT_100)
+#define LADSPA_IS_HINT_DEFAULT_440(x)     (((x) & LADSPA_HINT_DEFAULT_MASK)   \
+                                            == LADSPA_HINT_DEFAULT_440)
 
 typedef struct _LADSPA_PortRangeHint {
 
@@ -428,7 +519,7 @@ typedef struct _LADSPA_Descriptor {
      this function pointer must be set to NULL. When it is provided,
      the function set_run_adding_gain() must be provided also. */
   void (*run_adding)(LADSPA_Handle Instance,
-                    unsigned long SampleCount);
+                     unsigned long SampleCount);
 
   /* This method is a function pointer that sets the output gain for
      use when run_adding() is called (see above). If this function is
@@ -440,7 +531,7 @@ typedef struct _LADSPA_Descriptor {
      run_adding() function is provided. When it is absent this
      function pointer must be set to NULL. */
   void (*set_run_adding_gain)(LADSPA_Handle Instance,
-                             LADSPA_Data   Gain);
+                              LADSPA_Data   Gain);
 
   /* This is the counterpart to activate() (see above). If there is
      nothing for deactivate() to do then the plugin writer may provide
index dfde01e2b68ef901b43ae989d8d3c6dc12b20504..d8cebe51420f1cd77373bb3b4af235b4f9193108 100644 (file)
@@ -29,6 +29,7 @@
 #include <dirent.h>
 #include <dlfcn.h>
 #include <locale.h>
+#include <math.h>
 #include "pcm_local.h"
 #include "pcm_plugin.h"
 
@@ -48,37 +49,46 @@ 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;
+       /* Plugin custom fields */
        struct list_head pplugins;
        struct list_head cplugins;
-       unsigned int instances_channels;
-       snd_pcm_ladspa_instance_t **finstances;
+       unsigned int channels;                  /* forced input channels, 0 = auto */
+       unsigned int allocated;                 /* count of allocated samples */
+       LADSPA_Data *zero[2];                   /* zero input or dummy output */
 } snd_pcm_ladspa_t;
+typedef struct {
+        unsigned int size;
+        unsigned int *array;
+} snd_pcm_ladspa_array_t;
+
+typedef struct {
+        snd_pcm_ladspa_array_t channels;
+        snd_pcm_ladspa_array_t ports;
+       LADSPA_Data **m_data;
+        LADSPA_Data **data;
+} snd_pcm_ladspa_eps_t;
 
-struct snd_pcm_ladspa_instance {
+typedef 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;
-};
+       snd_pcm_ladspa_eps_t input;
+       snd_pcm_ladspa_eps_t output;
+       struct snd_pcm_ladspa_instance *prev;
+       struct snd_pcm_ladspa_instance *next;
+} snd_pcm_ladspa_instance_t;
 
 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 */
+       unsigned char *controls_initialized;    /* initialized by ALSA user */
        LADSPA_Data *controls;                  /* index = LADSPA control port index */
 } snd_pcm_ladspa_plugin_io_t;
 
@@ -90,11 +100,22 @@ typedef struct {
        const LADSPA_Descriptor *desc;
        snd_pcm_ladspa_plugin_io_t input;
        snd_pcm_ladspa_plugin_io_t output;
-       struct list_head instances;
+       struct list_head instances;             /* one LADSPA plugin might be used multiple times */
 } snd_pcm_ladspa_plugin_t;
 
 #endif /* DOC_HIDDEN */
 
+static unsigned int snd_pcm_ladspa_count_ports(snd_pcm_ladspa_plugin_t *lplug,
+                                               LADSPA_PortDescriptor pdesc)
+{
+        unsigned int res = 0, idx;
+        for (idx = 0; idx < lplug->desc->PortCount; idx++) {
+                if ((lplug->desc->PortDescriptors[idx] & pdesc) == pdesc)
+                        res++;
+        }
+        return res;
+}
+
 static int snd_pcm_ladspa_find_port(unsigned int *res,
                                    snd_pcm_ladspa_plugin_t *lplug,
                                    LADSPA_PortDescriptor pdesc,
@@ -146,10 +167,20 @@ static int snd_pcm_ladspa_find_port_idx(unsigned int *res,
        return 0;
 }
 
+static void snd_pcm_ladspa_free_io(snd_pcm_ladspa_plugin_io_t *io)
+{
+        if (io->controls)
+                free(io->controls);
+        if (io->controls_initialized)
+                free(io->controls_initialized);
+}
+
 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);
+                snd_pcm_ladspa_free_io(&plugin->input);
+                snd_pcm_ladspa_free_io(&plugin->output);
                if (plugin->dl_handle)
                        dlclose(plugin->dl_handle);
                if (plugin->filename)
@@ -161,8 +192,16 @@ static void snd_pcm_ladspa_free_plugins(struct list_head *plugins)
 
 static void snd_pcm_ladspa_free(snd_pcm_ladspa_t *ladspa)
 {
+        unsigned int idx;
+
        snd_pcm_ladspa_free_plugins(&ladspa->pplugins);
        snd_pcm_ladspa_free_plugins(&ladspa->cplugins);
+       for (idx = 0; idx < 2; idx++) {
+               if (ladspa->zero[idx])
+                       free(ladspa->zero[idx]);
+                ladspa->zero[idx] = NULL;
+        }
+        ladspa->allocated = 0;
 }
 
 static int snd_pcm_ladspa_close(snd_pcm_t *pcm)
@@ -175,7 +214,7 @@ static int snd_pcm_ladspa_close(snd_pcm_t *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;
+       snd_pcm_ladspa_t *ladspa = pcm->private_data;
        int err;
        snd_pcm_access_mask_t access_mask = { SND_PCM_ACCBIT_SHMN };
        err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_ACCESS,
@@ -188,19 +227,26 @@ static int snd_pcm_ladspa_hw_refine_cprepare(snd_pcm_t *pcm ATTRIBUTE_UNUSED, sn
        err = _snd_pcm_hw_params_set_subformat(params, SND_PCM_SUBFORMAT_STD);
        if (err < 0)
                return err;
+        if (ladspa->channels > 0 && pcm->stream == SND_PCM_STREAM_PLAYBACK) {
+               err = _snd_pcm_hw_param_set(params, SND_PCM_HW_PARAM_CHANNELS, ladspa->channels, 0);
+               if (err < 0)
+                       return err;
+        }
        params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
        return 0;
 }
 
 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_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);
        _snd_pcm_hw_params_set_format(sparams, SND_PCM_FORMAT_FLOAT);
        _snd_pcm_hw_params_set_subformat(sparams, SND_PCM_SUBFORMAT_STD);
+        if (ladspa->channels > 0 && pcm->stream == SND_PCM_STREAM_CAPTURE)
+                _snd_pcm_hw_param_set(sparams, SND_PCM_HW_PARAM_CHANNELS, ladspa->channels, 0);
        return 0;
 }
 
@@ -263,12 +309,19 @@ static int snd_pcm_ladspa_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params
        return 0;
 }
 
+static void snd_pcm_ladspa_free_eps(snd_pcm_ladspa_eps_t *eps)
+{
+       if (eps->channels.array)
+               free(eps->channels.array);
+        if (eps->ports.array)
+                free(eps->ports.array);
+}
+
 static void snd_pcm_ladspa_free_instances(snd_pcm_t *pcm, snd_pcm_ladspa_t *ladspa, int cleanup)
 {
        struct list_head *list, *pos, *pos1, *next1;
+       unsigned int idx;
        
-       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);
@@ -279,9 +332,21 @@ static void snd_pcm_ladspa_free_instances(snd_pcm_t *pcm, snd_pcm_ladspa_t *lads
                        if (cleanup) {
                                if (plugin->desc->cleanup)
                                        plugin->desc->cleanup(instance->handle);
-                               if (instance->m_data)
-                                       free(instance->m_data);
+                               if (instance->input.m_data) {
+                                       for (idx = 0; idx < instance->input.channels.size; idx++)
+                                               if (instance->input.m_data[idx])
+                                                       free(instance->input.m_data[idx]);
+                                       free(instance->input.m_data);
+                                }
+                               if (instance->output.m_data) {
+                                       for (idx = 0; idx < instance->output.channels.size; idx++)
+                                               if (instance->output.m_data[idx])
+                                                       free(instance->output.m_data[idx]);
+                                       free(instance->output.m_data);
+                                }
                                list_del(&(instance->list));
+                               snd_pcm_ladspa_free_eps(&instance->input);
+                               snd_pcm_ladspa_free_eps(&instance->output);
                                free(instance);
                        } else {
                                if (plugin->desc->activate)
@@ -292,58 +357,106 @@ static void snd_pcm_ladspa_free_instances(snd_pcm_t *pcm, snd_pcm_ladspa_t *lads
                        assert(list_empty(&plugin->instances));
                }
        }
-       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 ATTRIBUTE_UNUSED,
-                                 snd_pcm_ladspa_plugin_io_t *io,
-                                 snd_pcm_ladspa_instance_t *instance,
-                                 unsigned int channel,
-                                 unsigned int port)
+static int snd_pcm_ladspa_add_to_carray(snd_pcm_ladspa_array_t *array,
+                                        unsigned int idx,
+                                        unsigned int val)
 {
-       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;
+        unsigned int *narray;
+        unsigned int idx1;
+
+        if (idx >= array->size) {
+                narray = realloc(array->array, sizeof(unsigned int) * (idx + 1));
+                if (narray == NULL)
+                        return -ENOMEM;
+                for (idx1 = array->size; idx1 < idx; idx1++)
+                        narray[idx1] = NO_ASSIGN;
+                array->array = narray;
+                array->size = idx + 1;
+                array->array[idx] = val;
+                return 0;
+        }
+        if (array->array[idx] == NO_ASSIGN)
+                array->array[idx] = val;
+        else
+                return -EINVAL;
+        return 0;
 }
 
-static int snd_pcm_ladspa_connect_plugin(snd_pcm_ladspa_plugin_t *plugin,
-                                        snd_pcm_ladspa_plugin_io_t *io,
-                                        snd_pcm_ladspa_instance_t *instance,
-                                        unsigned int idx)
+static int snd_pcm_ladspa_add_to_array(snd_pcm_ladspa_array_t *array,
+                                       unsigned int idx,
+                                       unsigned int val)
 {
-       unsigned int port;
+        unsigned int *narray;
+        unsigned int idx1;
+
+        if (idx >= array->size) {
+                narray = realloc(array->array, sizeof(unsigned int) * (idx + 1));
+                if (narray == NULL)
+                        return -ENOMEM;
+                for (idx1 = array->size; idx1 < idx; idx1++)
+                        narray[idx1] = NO_ASSIGN;
+                array->array = narray;
+                array->size = idx + 1;
+        }
+        array->array[idx] = val;
+        return 0;
+}
+
+static int snd_pcm_ladspa_connect_plugin1(snd_pcm_ladspa_plugin_t *plugin,
+                                         snd_pcm_ladspa_plugin_io_t *io,
+                                         snd_pcm_ladspa_eps_t *eps)
+{
+       unsigned int port, channels, idx;
        int err;
 
        assert(plugin->policy == SND_PCM_LADSPA_POLICY_NONE);
-       if (io->port_bindings_size > 0) {
-               if (idx >= io->port_bindings_size)
-                       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 instance->channel != NO_ASSIGN ? err : 0;
+       channels = io->port_bindings_size > 0 ?
+                       io->port_bindings_size :
+                       snd_pcm_ladspa_count_ports(plugin, io->pdesc | LADSPA_PORT_AUDIO);
+       for (idx = 0; idx < channels; idx++) {
+               if (io->port_bindings_size > 0)
+                       port = io->port_bindings[idx];
+                else {
+                       err = snd_pcm_ladspa_find_port(&port, plugin, io->pdesc | LADSPA_PORT_AUDIO, idx);
+                       if (err < 0) {
+                               SNDERR("unable to find audio %s port %u plugin '%s'\n", io->pdesc & LADSPA_PORT_INPUT ? "input" : "output", idx, plugin->desc->Name);
+                               return err;
+                        }
+                }
+               err = snd_pcm_ladspa_add_to_carray(&eps->channels, idx, idx);
+               if (err < 0) {
+                       SNDERR("unable to add channel %u for audio %s plugin '%s'\n", idx, io->pdesc & LADSPA_PORT_INPUT ? "input" : "output", plugin->desc->Name);
+                       return err;
+                }
+               err = snd_pcm_ladspa_add_to_array(&eps->ports, idx, port);
+               if (err < 0) {
+                       SNDERR("unable to add port %u for audio %s plugin '%s'\n", port, io->pdesc & LADSPA_PORT_INPUT ? "input" : "output", plugin->desc->Name);
+                       return err;
+                }
        }
-       return snd_pcm_ladspa_connect(plugin, io, instance, idx, port);
+       return 0;
 }
 
-static int snd_pcm_ladspa_connect_plugin_duplicate(snd_pcm_ladspa_plugin_t *plugin,
-                                                  snd_pcm_ladspa_plugin_io_t *io,
-                                                  snd_pcm_ladspa_instance_t *instance,
-                                                  unsigned int idx)
+static int snd_pcm_ladspa_connect_plugin(snd_pcm_ladspa_plugin_t *plugin,
+                                        snd_pcm_ladspa_instance_t *instance)
+{
+        int err;
+        
+        err = snd_pcm_ladspa_connect_plugin1(plugin, &plugin->input, &instance->input);
+        if (err < 0)
+                return err;
+        err = snd_pcm_ladspa_connect_plugin1(plugin, &plugin->output, &instance->output);
+        if (err < 0)
+                return err;
+        return 0;
+}
+
+static int snd_pcm_ladspa_connect_plugin_duplicate1(snd_pcm_ladspa_plugin_t *plugin,
+                                                    snd_pcm_ladspa_plugin_io_t *io,
+                                                    snd_pcm_ladspa_eps_t *eps,
+                                                    unsigned int idx)
 {
        unsigned int port;
        int err;
@@ -353,10 +466,101 @@ static int snd_pcm_ladspa_connect_plugin_duplicate(snd_pcm_ladspa_plugin_t *plug
                port = io->port_bindings[0];
        } else {
                err = snd_pcm_ladspa_find_port(&port, plugin, io->pdesc | LADSPA_PORT_AUDIO, 0);
-               if (err < 0)
+               if (err < 0) {
+                       SNDERR("unable to find audio %s port %u plugin '%s'\n", io->pdesc & LADSPA_PORT_INPUT ? "input" : "output", (unsigned int)0, plugin->desc->Name);
                        return err;
+                }
        }
-       return snd_pcm_ladspa_connect(plugin, io, instance, idx, port);
+       err = snd_pcm_ladspa_add_to_carray(&eps->channels, 0, idx);
+       if (err < 0) {
+               SNDERR("unable to add channel %u for audio %s plugin '%s'\n", idx, io->pdesc & LADSPA_PORT_INPUT ? "input" : "output", plugin->desc->Name);
+               return err;
+        }
+        err = snd_pcm_ladspa_add_to_array(&eps->ports, 0, port);
+        if (err < 0) {
+               SNDERR("unable to add port %u for audio %s plugin '%s'\n", port, io->pdesc & LADSPA_PORT_INPUT ? "input" : "output", plugin->desc->Name);
+               return err;
+        }
+        return 0;
+}
+
+static int snd_pcm_ladspa_connect_plugin_duplicate(snd_pcm_ladspa_plugin_t *plugin,
+                                                  snd_pcm_ladspa_plugin_io_t *in_io,
+                                                  snd_pcm_ladspa_plugin_io_t *out_io,
+                                                  snd_pcm_ladspa_instance_t *instance,
+                                                  unsigned int idx)
+{
+       int err;
+
+       err = snd_pcm_ladspa_connect_plugin_duplicate1(plugin, in_io, &instance->input, idx);
+       if (err < 0)
+               return err;
+       err = snd_pcm_ladspa_connect_plugin_duplicate1(plugin, out_io, &instance->output, idx);
+       if (err < 0)
+               return err;
+        return 0;
+}
+
+static void snd_pcm_ladspa_get_default_cvalue(const LADSPA_Descriptor * desc, unsigned int port, LADSPA_Data *val) 
+{
+        LADSPA_PortRangeHintDescriptor hdesc;
+
+        hdesc = desc->PortRangeHints[port].HintDescriptor;
+        switch (hdesc & LADSPA_HINT_DEFAULT_MASK) {
+        case LADSPA_HINT_DEFAULT_MINIMUM:
+                *val = desc->PortRangeHints[port].LowerBound;
+                break;
+        case LADSPA_HINT_DEFAULT_LOW:
+                if (LADSPA_IS_HINT_LOGARITHMIC(hdesc)) {
+                        *val = exp(log(desc->PortRangeHints[port].LowerBound)
+                                        * 0.75
+                                        + log(desc->PortRangeHints[port].UpperBound)
+                                        * 0.25);
+                } else {
+                        *val = (desc->PortRangeHints[port].LowerBound * 0.75) +
+                               (desc->PortRangeHints[port].UpperBound * 0.25);
+                }
+                break;
+        case LADSPA_HINT_DEFAULT_MIDDLE:
+                if (LADSPA_IS_HINT_LOGARITHMIC(hdesc)) {
+                        *val = sqrt(desc->PortRangeHints[port].LowerBound *
+                                    desc->PortRangeHints[port].UpperBound);
+                } else {
+                        *val = 0.5 *
+                               (desc->PortRangeHints[port].LowerBound +
+                                desc->PortRangeHints[port].UpperBound);
+                }
+                break;
+        case LADSPA_HINT_DEFAULT_HIGH:
+                if (LADSPA_IS_HINT_LOGARITHMIC(hdesc)) {
+                        *val = exp(log(desc->PortRangeHints[port].LowerBound)
+                                        * 0.25
+                                        + log(desc->PortRangeHints[port].UpperBound)
+                                        * 0.75);
+                } else {
+                        *val = (desc->PortRangeHints[port].LowerBound * 0.25) +
+                               (desc->PortRangeHints[port].UpperBound * 0.75);
+                }
+                break;
+        case LADSPA_HINT_DEFAULT_MAXIMUM:
+                *val = desc->PortRangeHints[port].UpperBound;
+                break;
+        case LADSPA_HINT_DEFAULT_0:
+                *val = 0;
+                break;
+        case LADSPA_HINT_DEFAULT_1:
+                *val = 1;
+                break;
+        case LADSPA_HINT_DEFAULT_100:
+                *val = 100;
+                break;
+        case LADSPA_HINT_DEFAULT_440:
+                *val = 440;
+                break;
+        default:
+                *val = 0;      /* reasonable default, if everything fails */
+                break;
+        }
 }
 
 static int snd_pcm_ladspa_connect_controls(snd_pcm_ladspa_plugin_t *plugin,
@@ -368,6 +572,8 @@ static int snd_pcm_ladspa_connect_controls(snd_pcm_ladspa_plugin_t *plugin,
        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) {
+                               if (!io->controls_initialized[midx])
+                                       snd_pcm_ladspa_get_default_cvalue(plugin->desc, idx, &io->controls[midx]);
                                plugin->desc->connect_port(instance->handle, idx, &io->controls[midx]);
                        } else {
                                return -EINVAL;
@@ -377,30 +583,66 @@ static int snd_pcm_ladspa_connect_controls(snd_pcm_ladspa_plugin_t *plugin,
        return 0;
 }
 
+static int snd_pcm_ladspa_check_connect(snd_pcm_ladspa_plugin_t *plugin,
+                                        snd_pcm_ladspa_plugin_io_t *io,
+                                        snd_pcm_ladspa_eps_t *eps,
+                                        unsigned int depth)
+{
+        unsigned int idx, midx;
+        int err = 0;
+
+       for (idx = midx = 0; idx < plugin->desc->PortCount; idx++)
+               if ((plugin->desc->PortDescriptors[idx] & (io->pdesc | LADSPA_PORT_AUDIO)) == (io->pdesc | LADSPA_PORT_AUDIO)) {
+                        if (eps->channels.array[midx] == NO_ASSIGN) {
+                                SNDERR("%s port for plugin %s depth %u is not connected\n", io->pdesc & LADSPA_PORT_INPUT ? "input" : "output", plugin->desc->Name, depth);
+                                err++;
+                        }
+                       midx++;
+               }
+        if (err > 0) {
+                SNDERR("%i connection errors total\n", err);
+                return -EINVAL;
+        }
+        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 depth, idx, count;
-       snd_pcm_ladspa_instance_t *instance;
+        unsigned int in_channel, out_channel;
+        unsigned int in_channels, out_channels;
+       unsigned int in_ports, out_ports;
+       snd_pcm_ladspa_instance_t *instance = NULL;
        int err;
        
-       if (ladspa->instances_channels == 0)
-               return 0;
        list = pcm->stream == SND_PCM_STREAM_PLAYBACK ? &ladspa->pplugins : &ladspa->cplugins;
+       in_channels = ladspa->channels > 0 ? ladspa->channels :
+                     (pcm->stream == SND_PCM_STREAM_PLAYBACK ? pcm->channels : ladspa->plug.gen.slave->channels);
        depth = 0;
+       out_channels = 0;
        list_for_each(pos, list) {
                snd_pcm_ladspa_plugin_t *plugin = list_entry(pos, snd_pcm_ladspa_plugin_t, list);
+               if (pos->next == list)  /* last entry */
+                       out_channels = pcm->stream == SND_PCM_STREAM_PLAYBACK ? ladspa->plug.gen.slave->channels : pcm->channels;
+                in_ports = snd_pcm_ladspa_count_ports(plugin, LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO);
+                out_ports = snd_pcm_ladspa_count_ports(plugin, LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO);
                count = 1;
-               if (plugin->policy == SND_PCM_LADSPA_POLICY_DUPLICATE)
-                       count = pcm->channels;
-               for (idx = 0; idx < count; idx++) {
+               if (plugin->policy == SND_PCM_LADSPA_POLICY_DUPLICATE) {
+                        if (in_ports == 1 && out_ports == 1)
+                                count = in_channels;
+                        else
+                                plugin->policy = SND_PCM_LADSPA_POLICY_NONE;
+                }
+                in_channel = 0;
+                out_channel = 0;
+               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);
@@ -410,113 +652,167 @@ static int snd_pcm_ladspa_allocate_instances(snd_pcm_t *pcm, snd_pcm_ladspa_t *l
                        if (plugin->desc->activate)
                                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 depth %u", plugin->desc->Name, idx, instance->depth);
-                                       return err;
-                               }
-                               err = snd_pcm_ladspa_connect_plugin_duplicate(plugin, &plugin->output, instance, idx);
+                               err = snd_pcm_ladspa_connect_plugin_duplicate(plugin, &plugin->input, &plugin->output, instance, idx);
                                if (err < 0) {
-                                       SNDERR("Unable to connect duplicate output port of plugin '%s' channel %u depth %u", plugin->desc->Name, idx, instance->depth);
+                                       SNDERR("Unable to connect duplicate port of plugin '%s' channel %u depth %u", plugin->desc->Name, idx, instance->depth);
                                        return err;
                                }
+                       } else {
+                               err = snd_pcm_ladspa_connect_plugin(plugin, instance);
+                               if (err < 0) {
+                                       SNDERR("Unable to connect plugin '%s' depth %u", plugin->desc->Name, 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 = 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 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 depth %u", plugin->desc->Name, idx, depth);
-                                       return err;
-                               }
-                       }
-               }
+               err = snd_pcm_ladspa_check_connect(plugin, &plugin->input, &instance->input, depth);
+               if (err < 0)
+                       return err;
+               err = snd_pcm_ladspa_check_connect(plugin, &plugin->output, &instance->output, depth);
+               if (err < 0)
+                       return err;
                depth++;
        }
        return 0;
 }
 
-static int snd_pcm_ladspa_allocate_imemory(snd_pcm_ladspa_instance_t *instance, size_t alloc_size)
+static LADSPA_Data *snd_pcm_ladspa_allocate_zero(snd_pcm_ladspa_t *ladspa, unsigned int idx)
 {
-       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;
+        if (ladspa->zero[idx] == NULL)
+                ladspa->zero[idx] = calloc(ladspa->allocated, sizeof(LADSPA_Data));
+        return ladspa->zero[idx];
 }
 
 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;
+       snd_pcm_ladspa_instance_t *instance;
+       unsigned int channels = 16, nchannels;
+       unsigned int ichannels, ochannels;
+       void **pchannels, **npchannels;
+       unsigned int idx, chn;
        
-       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;
+        ladspa->allocated = 2048;
+        if (pcm->buffer_size > ladspa->allocated)
+                ladspa->allocated = pcm->buffer_size;
+        if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
+                ichannels = pcm->channels;
+                ochannels = ladspa->plug.gen.slave->channels;
+        } else {
+                ichannels = ladspa->plug.gen.slave->channels;
+                ochannels = pcm->channels;
+        }
+       pchannels = calloc(1, sizeof(void *) * channels);
+       if (pchannels == 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", instance->channel, plugin->desc->Name, 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;
+       list_for_each(pos, list) {
+               snd_pcm_ladspa_plugin_t *plugin = list_entry(pos, snd_pcm_ladspa_plugin_t, list);
+               list_for_each(pos1, &plugin->instances) {
+                       instance = list_entry(pos1, snd_pcm_ladspa_instance_t, list);
+                       nchannels = channels;
+                       for (idx = 0; idx < instance->input.channels.size; idx++) {
+                               chn = instance->input.channels.array[idx];
+                               if (chn >= nchannels)
+                                       nchannels = chn + 1;
+                        }
+                       for (idx = 0; idx < instance->output.channels.size; idx++) {
+                               chn = instance->output.channels.array[idx];
+                               if (chn >= nchannels)
+                                       nchannels = chn + 1;
+                        }
+                        if (nchannels != channels) {
+                                npchannels = realloc(pchannels, nchannels * sizeof(void *));
+                                if (npchannels == NULL) {
+                                        free(pchannels);
+                                        return -ENOMEM;
+                                }
+                                for (idx = channels; idx < nchannels; idx++)
+                                        npchannels[idx] = NULL;
+                                pchannels = npchannels;
+                        }
+                        assert(instance->input.data == NULL);
+                        assert(instance->input.m_data == NULL);
+                        assert(instance->output.data == NULL);
+                        assert(instance->output.m_data == NULL);
+                        instance->input.data = calloc(instance->input.channels.size, sizeof(void *));
+                        instance->input.m_data = calloc(instance->input.channels.size, sizeof(void *));
+                        instance->output.data = calloc(instance->output.channels.size, sizeof(void *));
+                        instance->output.m_data = calloc(instance->output.channels.size, sizeof(void *));
+                        if (instance->input.data == NULL ||
+                            instance->input.m_data == NULL ||
+                            instance->output.data == NULL ||
+                            instance->output.m_data == NULL)
+                                return -ENOMEM;
+                       for (idx = 0; idx < instance->input.channels.size; idx++) {
+                               chn = instance->output.channels.array[idx];
+                               if (pchannels[chn] == NULL && chn < ichannels) {
+                                       instance->input.data[idx] = NULL;
+                                       continue;
+                                }
+                               instance->input.data[idx] = pchannels[chn];
+                               if (instance->input.data[idx] == NULL) {
+                                        instance->input.data[idx] = snd_pcm_ladspa_allocate_zero(ladspa, 0);
+                                        if (instance->input.data[idx] == NULL)
+                                                return -ENOMEM;
+                                }
+                        }
+                        for (idx = 0; idx < instance->output.channels.size; idx++) {
+                               chn = instance->output.channels.array[idx];
+                                /* FIXME/OPTIMIZE: check if we can remove double alloc */
+                                /* if LADSPA plugin has no broken inplace */
+                                instance->output.data[idx] = malloc(sizeof(LADSPA_Data) * ladspa->allocated);
+                                if (instance->output.data[idx] == NULL)
+                                        return -ENOMEM;
+                                pchannels[chn] = instance->output.m_data[idx] = instance->output.data[idx];
+                        }
                }
        }
-       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;
+       /* OPTIMIZE: we have already allocated areas for ALSA output channels */
+       /* next loop deallocates the last output LADSPA areas and connects */
+       /* them to ALSA areas (NULL) or dummy area ladpsa->free[1] ; */
+       /* this algorithm might be optimized to not allocate the last LADSPA outputs */
+       list_for_each(pos, list) {
+               snd_pcm_ladspa_plugin_t *plugin = list_entry(pos, snd_pcm_ladspa_plugin_t, list);
+               list_for_each(pos1, &plugin->instances) {
+                       instance = list_entry(pos1, snd_pcm_ladspa_instance_t, list);
+                        for (idx = 0; idx < instance->output.channels.size; idx++) {
+                               chn = instance->output.channels.array[idx];
+                                if (instance->output.data[idx] == pchannels[chn]) {
+                                        if (instance->output.m_data[idx]) {
+                                                free(instance->output.m_data[idx]);
+                                                instance->output.m_data[idx] = NULL;
+                                        }
+                                        if (chn < ochannels) {
+                                                instance->output.data[idx] = NULL;
+                                        } else {
+                                                instance->output.data[idx] = snd_pcm_ladspa_allocate_zero(ladspa, 1);
+                                                if (instance->output.data[idx] == NULL)
+                                                        return -ENOMEM;
+                                        }
+                                }
+                        }
+                }
+        }
+#if 0
+        printf("zero[0] = %p\n", ladspa->zero[0]);
+        printf("zero[1] = %p\n", ladspa->zero[1]);
+       list_for_each(pos, list) {
+               snd_pcm_ladspa_plugin_t *plugin = list_entry(pos, snd_pcm_ladspa_plugin_t, list);
+               list_for_each(pos1, &plugin->instances) {
+                       instance = list_entry(pos1, snd_pcm_ladspa_instance_t, list);
+                        for (idx = 0; idx < instance->input.channels.size; idx++)
+                                printf("%i:alloc-input%i:  data = %p, m_data = %p\n", instance->depth, idx, instance->input.data[idx], instance->input.m_data[idx]);
+                        for (idx = 0; idx < instance->output.channels.size; idx++)
+                                printf("%i:alloc-output%i:  data = %p, m_data = %p\n", instance->depth, idx, instance->output.data[idx], instance->output.m_data[idx]);
+               }
        }
+#endif
+       free(pchannels);
        return 0;
 }
 
@@ -525,21 +821,16 @@ static int snd_pcm_ladspa_init(snd_pcm_t *pcm)
        snd_pcm_ladspa_t *ladspa = pcm->private_data;
        int err;
        
-       if (pcm->channels != ladspa->instances_channels) {
+       snd_pcm_ladspa_free_instances(pcm, ladspa, 1);
+       err = snd_pcm_ladspa_allocate_instances(pcm, ladspa);
+       if (err < 0) {
                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);
+               return err;
+       }
+       err = snd_pcm_ladspa_allocate_memory(pcm, ladspa);
+       if (err < 0) {
+               snd_pcm_ladspa_free_instances(pcm, ladspa, 1);
+               return err;
        }
        return 0;
 }
@@ -562,40 +853,55 @@ snd_pcm_ladspa_write_areas(snd_pcm_t *pcm,
                           snd_pcm_uframes_t *slave_sizep)
 {
        snd_pcm_ladspa_t *ladspa = pcm->private_data;
-       unsigned int channel;
+       snd_pcm_ladspa_instance_t *instance;
+       struct list_head *pos, *pos1;
+       LADSPA_Data *data;
+       unsigned int idx, chn, size1, size2;
        
        if (size > *slave_sizep)
                size = *slave_sizep;
-#if 0  // no processing - for testing purposes only
+        size2 = size;
+#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;
-               }
+        while (size > 0) {
+                size1 = size;
+                if (size1 > ladspa->allocated)
+                        size1 = ladspa->allocated;
+               list_for_each(pos, &ladspa->pplugins) {
+                       snd_pcm_ladspa_plugin_t *plugin = list_entry(pos, snd_pcm_ladspa_plugin_t, list);
+                       list_for_each(pos1, &plugin->instances) {
+                               instance = list_entry(pos1, snd_pcm_ladspa_instance_t, list);
+                               for (idx = 0; idx < instance->input.channels.size; idx++) {
+                                        chn = instance->input.channels.array[idx];
+                                        data = instance->input.data[idx];
+                                        if (data == NULL) {
+                                               data = (LADSPA_Data *)((char *)areas[chn].addr + (areas[chn].first / 8));
+                                                       data += offset;
+                                        }      
+                                       instance->desc->connect_port(instance->handle, instance->input.ports.array[idx], data);
+                               }
+                               for (idx = 0; idx < instance->output.channels.size; idx++) {
+                                        chn = instance->output.channels.array[idx];
+                                        data = instance->output.data[idx];
+                                        if (data == NULL) {
+                                               data = (LADSPA_Data *)((char *)slave_areas[chn].addr + (areas[chn].first / 8));
+                                               data += slave_offset;
+                                        }
+                                       instance->desc->connect_port(instance->handle, instance->output.ports.array[idx], data);
+                               }
+                               instance->desc->run(instance->handle, size1);
+                       }
+               }
+               offset += size1;
+               slave_offset += size1;
+               size -= size1;
        }
 #endif
-       *slave_sizep = size;
-       return size;
+       *slave_sizep = size2;
+       return size2;
 }
 
 static snd_pcm_uframes_t
@@ -608,45 +914,62 @@ snd_pcm_ladspa_read_areas(snd_pcm_t *pcm,
                          snd_pcm_uframes_t *slave_sizep)
 {
        snd_pcm_ladspa_t *ladspa = pcm->private_data;
-       unsigned int channel;
+       snd_pcm_ladspa_instance_t *instance;
+       struct list_head *pos, *pos1;
+       LADSPA_Data *data;
+       unsigned int idx, chn, size1, size2;;
 
        if (size > *slave_sizep)
                size = *slave_sizep;
-#if 0  // no processing - for testing purposes only
+        size2 = size;
+#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;
-               }
+        while (size > 0) {
+                size1 = size;
+                if (size1 > ladspa->allocated)
+                        size1 = ladspa->allocated;
+               list_for_each(pos, &ladspa->cplugins) {
+                       snd_pcm_ladspa_plugin_t *plugin = list_entry(pos, snd_pcm_ladspa_plugin_t, list);
+                       list_for_each(pos1, &plugin->instances) {
+                               instance = list_entry(pos1, snd_pcm_ladspa_instance_t, list);
+                               for (idx = 0; idx < instance->input.channels.size; idx++) {
+                                        chn = instance->input.channels.array[idx];
+                                        data = instance->input.data[idx];
+                                        if (data == NULL) {
+                                               data = (LADSPA_Data *)((char *)slave_areas[chn].addr + (areas[chn].first / 8));
+                                               data += slave_offset;
+                                        }      
+                                       instance->desc->connect_port(instance->handle, instance->input.ports.array[idx], data);
+                               }
+                               for (idx = 0; idx < instance->output.channels.size; idx++) {
+                                        chn = instance->output.channels.array[idx];
+                                        data = instance->output.data[idx];
+                                        if (data == NULL) {
+                                               data = (LADSPA_Data *)((char *)areas[chn].addr + (areas[chn].first / 8));
+                                                       data += offset;
+                                        }
+                                       instance->desc->connect_port(instance->handle, instance->output.ports.array[idx], data);
+                               }
+                               instance->desc->run(instance->handle, size1);
+                       }
+               }
+               offset += size1;
+               slave_offset += size1;
+               size -= size1;
        }
 #endif
-       *slave_sizep = size;
-       return size;
+       *slave_sizep = size2;
+       return size2;
 }
 
-static void snd_pcm_ladspa_dump_direction(snd_pcm_ladspa_plugin_io_t *io, snd_output_t *out)
+static void snd_pcm_ladspa_dump_direction(snd_pcm_ladspa_plugin_t *plugin,
+                                          snd_pcm_ladspa_plugin_io_t *io,
+                                          snd_output_t *out)
 {
-       unsigned int idx;
+       unsigned int idx, midx;
 
        if (io->port_bindings_size == 0)
                goto __control;
@@ -661,8 +984,34 @@ static void snd_pcm_ladspa_dump_direction(snd_pcm_ladspa_plugin_io_t *io, snd_ou
        if (io->controls_size == 0)
                return;
        snd_output_printf(out, "    Control %s port initial values:\n", io->pdesc == LADSPA_PORT_INPUT ? "input" : "output");
-       for (idx = 0; idx < io->controls_size; idx++)
-               snd_output_printf(out, "      %i = %.8f\n", idx, io->controls[idx]);
+       for (idx = midx = 0; idx < plugin->desc->PortCount; idx++) {
+               if ((plugin->desc->PortDescriptors[idx] & (io->pdesc | LADSPA_PORT_CONTROL)) == (io->pdesc | LADSPA_PORT_CONTROL)) {
+                       snd_output_printf(out, "      %i \"%s\" = %.8f\n", idx, plugin->desc->PortNames[idx], io->controls[midx]);
+                       midx++;
+                }
+        }
+}
+
+static void snd_pcm_ladspa_dump_array(snd_output_t *out,
+                                      snd_pcm_ladspa_array_t *array,
+                                      snd_pcm_ladspa_plugin_t *plugin)
+{
+        unsigned int size = array->size;
+        unsigned int val, idx = 0;
+
+        while (size-- > 0) {
+                if (idx > 0) {
+                        snd_output_putc(out, ',');
+                        snd_output_putc(out, ' ');
+                }
+                val = array->array[idx++];
+                if (val == NO_ASSIGN)
+                        snd_output_putc(out, '-');
+                else
+                        snd_output_printf(out, "%u", val);
+                if (plugin)
+                        snd_output_printf(out, " \"%s\"", plugin->desc->PortNames[val]);
+        }
 }
 
 static void snd_pcm_ladspa_plugins_dump(struct list_head *list, snd_output_t *out)
@@ -673,14 +1022,25 @@ static void snd_pcm_ladspa_plugins_dump(struct list_head *list, snd_output_t *ou
                snd_pcm_ladspa_plugin_t *plugin = list_entry(pos, snd_pcm_ladspa_plugin_t, list);
                snd_output_printf(out, "    Policy: %s\n", plugin->policy == SND_PCM_LADSPA_POLICY_NONE ? "none" : "duplicate");
                snd_output_printf(out, "    Filename: %s\n", plugin->filename);
+               snd_output_printf(out, "    Plugin Name: %s\n", plugin->desc->Name);
+               snd_output_printf(out, "    Plugin Label: %s\n", plugin->desc->Label);
+               snd_output_printf(out, "    Plugin Unique ID: %lu\n", plugin->desc->UniqueID);
                 snd_output_printf(out, "    Instances:\n");
                list_for_each(pos2, &plugin->instances) {
                        snd_pcm_ladspa_instance_t *in = (snd_pcm_ladspa_instance_t *) pos2;
-                       snd_output_printf(out, "      Depth: %i, Channel: %i, Input: %i, Output: %i\n",
-                                         in->depth, in->channel, in->in_port, in->out_port);
+                       snd_output_printf(out, "      Depth: %i\n", in->depth);
+                       snd_output_printf(out, "         InChannels: ");
+                        snd_pcm_ladspa_dump_array(out, &in->input.channels, NULL);
+                        snd_output_printf(out, "\n         InPorts: ");
+                        snd_pcm_ladspa_dump_array(out, &in->input.ports, plugin);
+                        snd_output_printf(out, "\n         OutChannels: ");
+                        snd_pcm_ladspa_dump_array(out, &in->output.channels, NULL);
+                        snd_output_printf(out, "\n         OutPorts: ");
+                        snd_pcm_ladspa_dump_array(out, &in->output.ports, plugin);
+                        snd_output_printf(out, "\n");
                }
-               snd_pcm_ladspa_dump_direction(&plugin->input, out);
-               snd_pcm_ladspa_dump_direction(&plugin->output, out);
+               snd_pcm_ladspa_dump_direction(plugin, &plugin->input, out);
+               snd_pcm_ladspa_dump_direction(plugin, &plugin->output, out);
        }
 }
 
@@ -689,16 +1049,6 @@ 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_output_printf(out, "  Instances setup:\n");
-       snd_output_printf(out, "    Channels: %i\n", ladspa->instances_channels);
-       if (ladspa->finstances) {
-               unsigned int idx;
-               for (idx = 0; idx < ladspa->instances_channels; idx++) {
-                       snd_pcm_ladspa_instance_t *in = ladspa->finstances[idx];
-                       snd_output_printf(out, "    Instance %i, Depth: %i, Channel: %i\n",
-                                        idx, in->depth, in->channel);
-                }
-       }
        snd_output_printf(out, "  Playback:\n");
        snd_pcm_ladspa_plugins_dump(&ladspa->pplugins, out);
        snd_output_printf(out, "  Capture:\n");
@@ -946,51 +1296,61 @@ static int snd_pcm_ladspa_parse_ioconfig(snd_pcm_ladspa_plugin_t *lplug,
                        }
                }
        }
-       if (controls) {
+       if (1) {
                unsigned int count = 0;
                LADSPA_Data *array;
+               unsigned char *initialized;
                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));
                if (!array)
                        return -ENOMEM;
+               initialized = (unsigned char *)calloc(count, sizeof(unsigned char));
+               if (!initialized) {
+                       free(array);
+                       return -ENOMEM;
+                }
                io->controls_size = count;
+               io->controls_initialized = initialized;
                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, uval;
-                       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;
-                       }
-                       err = snd_pcm_ladspa_find_port_idx(&uval, lplug, io->pdesc | LADSPA_PORT_CONTROL, port);
-                       if (err < 0) {
-                               SNDERR("internal error");
-                               return err;
-                       }
-                       array[uval] = (LADSPA_Data)dval;
-               }
+               if (!(io->pdesc & LADSPA_PORT_OUTPUT)) {
+                       if (snd_config_get_type(controls) != SND_CONFIG_TYPE_COMPOUND) {
+                               SNDERR("controls definition must be a compound");
+                               return -EINVAL;
+                       }
+                       snd_config_for_each(i, next, controls) {
+                               snd_config_t *n = snd_config_iterator_entry(i);
+                               const char *id;
+                               long lval;
+                               unsigned int port, uval;
+                               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;
+                               }
+                               err = snd_pcm_ladspa_find_port_idx(&uval, lplug, io->pdesc | LADSPA_PORT_CONTROL, port);
+                               if (err < 0) {
+                                       SNDERR("internal error");
+                                       return err;
+                               }
+                               initialized[uval] = 1;
+                               array[uval] = (LADSPA_Data)dval;
+                       }
+                }
        }
        return 0;
 }
@@ -1145,6 +1505,7 @@ static int snd_pcm_ladspa_build_plugins(struct list_head *list,
  * \param pcmp Returns created PCM handle
  * \param name Name of PCM
  * \param ladspa_path The path for LADSPA plugins
+ * \param channels Force input channel count to LADSPA plugin chain, 0 = no force (auto)
  * \param ladspa_pplugins The playback configuration
  * \param ladspa_cplugins The capture configuration
  * \param slave Slave PCM handle
@@ -1156,6 +1517,7 @@ static int snd_pcm_ladspa_build_plugins(struct list_head *list,
  */
 int snd_pcm_ladspa_open(snd_pcm_t **pcmp, const char *name,
                        const char *ladspa_path,
+                       unsigned int channels,
                        snd_config_t *ladspa_pplugins,
                        snd_config_t *ladspa_cplugins,
                        snd_pcm_t *slave, int close_slave)
@@ -1182,6 +1544,7 @@ int snd_pcm_ladspa_open(snd_pcm_t **pcmp, const char *name,
 
        INIT_LIST_HEAD(&ladspa->pplugins);
        INIT_LIST_HEAD(&ladspa->cplugins);
+       ladspa->channels = channels;
 
        if (slave->stream == SND_PCM_STREAM_PLAYBACK) {
                err = snd_pcm_ladspa_build_plugins(&ladspa->pplugins, ladspa_path, ladspa_pplugins, reverse);
@@ -1227,6 +1590,16 @@ can be either little or big-endian depending on architecture).
 
 The policy duplicate means that there must be only one binding definition for
 channel zero. This definition is automatically duplicated for all channels.
+If the LADSPA plugin has multiple audio inputs or outputs the policy duplicate
+is automatically switched to policy none.
+
+The plugin serialization works as expected. You can eventually use more
+channels (inputs / outputs) inside the LADPSA plugin chain than processed
+in the ALSA plugin chain. If ALSA channel does not exist for given LADSPA
+input audio port, zero samples are given to this LADSPA port. On the output
+side (ALSA next plugin input), the valid channels are checked, too.
+If specific ALSA channel does not exist, the LADSPA output port is
+connected to a dummy sample area.
 
 Instances of LADSPA plugins are created dynamically.
 
@@ -1240,6 +1613,7 @@ pcm.name {
                 # or
                 pcm { }         # Slave PCM definition
         }
+        [channels INT]         # count input channels (input to LADSPA plugin chain)
        [path STR]              # Path (directory) with LADSPA plugins
        plugins |               # Definition for both directions
         playback_plugins |     # Definition for playback direction
@@ -1254,6 +1628,7 @@ pcm.name {
                                        C INT or STR    # C - channel, INT - audio port index, STR - audio port name
                                }
                                controls {
+                                       # valid only in the input block
                                        I INT or REAL   # I - control port index, INT or REAL - control value
                                        # or
                                        STR INT or REAL # STR - control port name, INT or REAL - control value
@@ -1295,6 +1670,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;
+       long channels = 0;
        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);
@@ -1311,6 +1687,14 @@ int _snd_pcm_ladspa_open(snd_pcm_t **pcmp, const char *name,
                        snd_config_get_string(n, &path);
                        continue;
                }
+               if (strcmp(id, "channels") == 0) {
+                       snd_config_get_integer(n, &channels);
+                       if (channels > 1024)
+                               channels = 1024;
+                        if (channels < 0)
+                                channels = 0;
+                       continue;
+               }
                if (strcmp(id, "plugins") == 0) {
                        plugins = n;
                        continue;
@@ -1345,7 +1729,7 @@ int _snd_pcm_ladspa_open(snd_pcm_t **pcmp, const char *name,
        snd_config_delete(sconf);
        if (err < 0)
                return err;
-       err = snd_pcm_ladspa_open(pcmp, name, path, pplugins, cplugins, spcm, 1);
+       err = snd_pcm_ladspa_open(pcmp, name, path, channels, pplugins, cplugins, spcm, 1);
        if (err < 0)
                snd_pcm_close(spcm);
        return err;