#include <dirent.h>
#include <dlfcn.h>
#include <locale.h>
+#include <math.h>
#include "pcm_local.h"
#include "pcm_plugin.h"
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;
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,
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)
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)
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,
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;
}
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);
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)
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;
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,
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;
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);
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;
}
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;
}
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
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;
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)
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);
}
}
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");
}
}
}
- 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;
}
* \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
*/
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)
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);
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.
# 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
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
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);
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;
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;