* ladspa plugins (patch)
@ 2006-01-19 21:00 Nathan Kurz
2006-01-20 8:18 ` Jaroslav Kysela
0 siblings, 1 reply; 2+ messages in thread
From: Nathan Kurz @ 2006-01-19 21:00 UTC (permalink / raw)
To: alsa-devel
[-- Attachment #1: Type: text/plain, Size: 2494 bytes --]
Hello ---
I just started trying to use some LADSPA plugins to allow for
pre-processing of input before it is seen by a soft-phone application,
and encountered some problems. Some plugins would work, and others
would fail with an assertion in snd_pcm_ladspa_allocate:
pcm.pitchshift {
type ladspa
slave.pcm "plughw:0,0"
path "/usr/local/lib/ladspa"
plugins [
{
label pitchScaleHQ
input { controls [ 2 ] }
}
]
}
$ arecord -f dat -d 4 -Dplug:pitchshift foo.wav
Recording WAVE 'foo.wav' : Signed 16 bit Little Endian,
Rate 48000 Hz, Stereo
pcm_ladspa.c:670: snd_pcm_ladspa_allocate_instances:
Assertion `err >= 0' failed.
It turned out that plugins that had control outputs were not being set
up properly if there was no corresponding "output" section. Adding an
output section (even if entirely empty) solved this problem:
pcm.pitchshift {
type ladspa
slave.pcm "plughw:0,0"
path "/usr/local/lib/ladspa"
plugins [
{
label pitchScaleHQ
input { controls [ 2 ] }
output { }
}
]
}
This behavior seemed to be somewhere between buggy and undocumented,
so I wrote a patch that causes the proper controls to be set up even
for both input and output even if the ioconfig section is omitted. I
think this is significantly better than the current behavior, which is
to perform differently with an empty ioconfig than with an absent one:
pcm.pitchshift {
type ladspa
slave.pcm "plughw:0,0"
path "/usr/local/lib/ladspa"
plugins [
{
# input {} optional if defaults are used
label pitchScaleHQ
}
]
}
The attached patch looks more complicated than it actually is. What
it does is to split snd_pcm_ladspa_parse_ioconfig() into pieces so it
calls three simpler functions: snd_pcm_ladspa_add_default_controls(),
snd_pcm_ladspa_parse_controls(), and snd_pcm_ladspa_parse_bindings().
'add_default_controls' is always called, and the other two are called
only if their corresponding section exists.
I haven't tested this patch well, but compiles without error and
appears to be working on my system. While not perfect, I think the
functions I've created are clearer than what came before. I tried to
follow the established coding patterns, and apologize if I haven't
succeeded. The patch is against alsa-lib-1.0.11rc2.
I hope this is useful enough for inclusion in CVS,
Nathan Kurz
nate@verse.com
[-- Attachment #2: ladspa.patch --]
[-- Type: text/plain, Size: 10131 bytes --]
--- src/pcm/pcm_ladspa.c.orig 2006-01-19 09:19:36.000000000 -0700
+++ src/pcm/pcm_ladspa.c 2006-01-19 13:49:32.000000000 -0700
@@ -1205,155 +1205,204 @@
return -ENOENT;
}
-static int snd_pcm_ladspa_parse_ioconfig(snd_pcm_ladspa_plugin_t *lplug,
+static int snd_pcm_ladspa_add_default_controls(snd_pcm_ladspa_plugin_t *lplug,
+ snd_pcm_ladspa_plugin_io_t *io)
+{
+ unsigned int count = 0;
+ LADSPA_Data *array;
+ unsigned char *initialized;
+ unsigned long idx;
+
+ 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;
+
+ return 0;
+}
+
+static int snd_pcm_ladspa_parse_controls(snd_pcm_ladspa_plugin_t *lplug,
snd_pcm_ladspa_plugin_io_t *io,
- snd_config_t *conf)
+ snd_config_t *controls)
{
snd_config_iterator_t i, next;
- snd_config_t *bindings = NULL, *controls = NULL;
int err;
-
- if (conf == NULL)
- return 0;
- if (snd_config_get_type(conf) != SND_CONFIG_TYPE_COMPOUND) {
- SNDERR("input or output definition must be a compound");
+
+ 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, conf) {
+
+ 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;
- if (strcmp(id, "bindings") == 0) {
- bindings = n;
- 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 (strcmp(id, "controls") == 0) {
- controls = n;
- continue;
+ 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;
+ }
+ io->controls_initialized[uval] = 1;
+ io->controls[uval] = (LADSPA_Data)dval;
}
- if (bindings) {
- unsigned int count = 0;
- unsigned int *array;
- if (snd_config_get_type(bindings) != SND_CONFIG_TYPE_COMPOUND) {
- SNDERR("bindings definition must be a compound");
+
+ return 0;
+}
+
+static int snd_pcm_ladspa_parse_bindings(snd_pcm_ladspa_plugin_t *lplug,
+ snd_pcm_ladspa_plugin_io_t *io,
+ snd_config_t *bindings)
+{
+ unsigned int count = 0;
+ unsigned int *array;
+ snd_config_iterator_t i, next;
+ int err;
+
+ if (snd_config_get_type(bindings) != SND_CONFIG_TYPE_COMPOUND) {
+ SNDERR("bindings definition must be a compound");
+ return -EINVAL;
+ }
+ snd_config_for_each(i, next, bindings) {
+ snd_config_t *n = snd_config_iterator_entry(i);
+ const char *id;
+ long channel;
+ if (snd_config_get_id(n, &id) < 0)
+ continue;
+ err = safe_strtol(id, &channel);
+ if (err < 0 || channel < 0) {
+ SNDERR("Invalid channel number: %s", id);
return -EINVAL;
}
+ if (lplug->policy == SND_PCM_LADSPA_POLICY_DUPLICATE && channel > 0) {
+ SNDERR("Wrong channel specification for duplicate policy");
+ return -EINVAL;
+ }
+ if (count < (unsigned int)(channel + 1))
+ count = (unsigned int)(channel + 1);
+ }
+ if (count > 0) {
+ array = (unsigned int *)calloc(count, sizeof(unsigned int));
+ if (! array)
+ return -ENOMEM;
+ memset(array, 0xff, count * sizeof(unsigned int));
+ io->port_bindings_size = count;
+ io->port_bindings = array;
snd_config_for_each(i, next, bindings) {
snd_config_t *n = snd_config_iterator_entry(i);
- const char *id;
- long channel;
+ const char *id, *sport;
+ long channel, port;
if (snd_config_get_id(n, &id) < 0)
continue;
err = safe_strtol(id, &channel);
if (err < 0 || channel < 0) {
- SNDERR("Invalid channel number: %s", id);
- return -EINVAL;
- }
- if (lplug->policy == SND_PCM_LADSPA_POLICY_DUPLICATE && channel > 0) {
- SNDERR("Wrong channel specification for duplicate policy");
+ assert(0); /* should never happen */
return -EINVAL;
}
- if (count < (unsigned int)(channel + 1))
- count = (unsigned int)(channel + 1);
- }
- if (count > 0) {
- array = (unsigned int *)calloc(count, sizeof(unsigned int));
- if (! array)
- return -ENOMEM;
- memset(array, 0xff, count * sizeof(unsigned int));
- io->port_bindings_size = count;
- io->port_bindings = array;
- snd_config_for_each(i, next, bindings) {
- snd_config_t *n = snd_config_iterator_entry(i);
- const char *id, *sport;
- long channel, port;
- if (snd_config_get_id(n, &id) < 0)
- continue;
- err = safe_strtol(id, &channel);
- if (err < 0 || channel < 0) {
- assert(0); /* should never happen */
- return -EINVAL;
- }
- err = snd_config_get_integer(n, &port);
- if (err >= 0) {
- err = snd_pcm_ladspa_find_port(&array[channel], lplug, io->pdesc | LADSPA_PORT_AUDIO, port);
- if (err < 0) {
- SNDERR("Unable to find an audio port (%li) for channel %s", port, id);
- return err;
- }
- continue;
- }
- err = snd_config_get_string(n, &sport);
- if (err < 0) {
- SNDERR("Invalid LADSPA port field type for %s", id);
- return -EINVAL;
- }
- err = snd_pcm_ladspa_find_sport(&array[channel], lplug, io->pdesc | LADSPA_PORT_AUDIO, sport);
+ err = snd_config_get_integer(n, &port);
+ if (err >= 0) {
+ err = snd_pcm_ladspa_find_port(&array[channel], lplug, io->pdesc | LADSPA_PORT_AUDIO, port);
if (err < 0) {
- SNDERR("Unable to find an audio port (%s) for channel %s", sport, id);
+ SNDERR("Unable to find an audio port (%li) for channel %s", port, id);
return err;
}
+ continue;
+ }
+ err = snd_config_get_string(n, &sport);
+ if (err < 0) {
+ SNDERR("Invalid LADSPA port field type for %s", id);
+ return -EINVAL;
+ }
+ err = snd_pcm_ladspa_find_sport(&array[channel], lplug, io->pdesc | LADSPA_PORT_AUDIO, sport);
+ if (err < 0) {
+ SNDERR("Unable to find an audio port (%s) for channel %s", sport, id);
+ return err;
}
}
}
- if (1) {
- unsigned int count = 0;
- LADSPA_Data *array;
- unsigned char *initialized;
- unsigned long idx;
- 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;
- 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;
+}
+
+static int snd_pcm_ladspa_parse_ioconfig(snd_pcm_ladspa_plugin_t *lplug,
+ snd_pcm_ladspa_plugin_io_t *io,
+ snd_config_t *conf)
+{
+ snd_config_iterator_t i, next;
+ snd_config_t *bindings = NULL, *controls = NULL;
+ int err;
+
+ /* always add default controls for both input and output */
+ err = snd_pcm_ladspa_add_default_controls(lplug, io);
+ if (err < 0) {
+ SNDERR("error adding default controls");
+ return err;
}
+
+ if (conf == NULL) {
+ return 0;
+ }
+
+ if (snd_config_get_type(conf) != SND_CONFIG_TYPE_COMPOUND) {
+ SNDERR("input or output definition must be a compound");
+ return -EINVAL;
+ }
+ snd_config_for_each(i, next, conf) {
+ snd_config_t *n = snd_config_iterator_entry(i);
+ const char *id;
+ if (snd_config_get_id(n, &id) < 0)
+ continue;
+ if (strcmp(id, "bindings") == 0) {
+ bindings = n;
+ continue;
+ }
+ if (strcmp(id, "controls") == 0) {
+ controls = n;
+ continue;
+ }
+ }
+
+ /* ignore values of parameters for output controls */
+ if (controls && !(io->pdesc & LADSPA_PORT_OUTPUT)) {
+ err = snd_pcm_ladspa_parse_controls(lplug, io, controls);
+ if (err < 0)
+ return err;
+ }
+
+ if (bindings) {
+ err = snd_pcm_ladspa_parse_bindings(lplug, io, bindings);
+ if (err < 0)
+ return err;
+ }
+
+
return 0;
}
^ permalink raw reply [flat|nested] 2+ messages in thread
* Re: ladspa plugins (patch)
2006-01-19 21:00 ladspa plugins (patch) Nathan Kurz
@ 2006-01-20 8:18 ` Jaroslav Kysela
0 siblings, 0 replies; 2+ messages in thread
From: Jaroslav Kysela @ 2006-01-20 8:18 UTC (permalink / raw)
To: Nathan Kurz; +Cc: alsa-devel
On Thu, 19 Jan 2006, Nathan Kurz wrote:
> I haven't tested this patch well, but compiles without error and
> appears to be working on my system. While not perfect, I think the
> functions I've created are clearer than what came before. I tried to
> follow the established coding patterns, and apologize if I haven't
> succeeded. The patch is against alsa-lib-1.0.11rc2.
>
> I hope this is useful enough for inclusion in CVS,
Thanks. It works for my test configuration, too. I applied it to CVS.
Jaroslav
-----
Jaroslav Kysela <perex@suse.cz>
Linux Kernel Sound Maintainer
ALSA Project, SUSE Labs
-------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc. Do you grep through log files
for problems? Stop! Download the new AJAX search engine that makes
searching your log files as easy as surfing the web. DOWNLOAD SPLUNK!
http://sel.as-us.falkag.net/sel?cmd=lnk&kid=103432&bid=230486&dat=121642
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2006-01-20 8:18 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2006-01-19 21:00 ladspa plugins (patch) Nathan Kurz
2006-01-20 8:18 ` Jaroslav Kysela
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.