summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorivo <ivo@b3059339-0415-0410-9bf9-f77b7e298cf2>2004-12-23 02:09:52 +0000
committerivo <ivo@b3059339-0415-0410-9bf9-f77b7e298cf2>2004-12-23 02:09:52 +0000
commit3d4852d8e029c37902dfd83c8c7c46ba0c16cb37 (patch)
treee0284cf1d93f99a386c95ee6fadbe9891f3697b7
parent7cdcc45e705bd2173c6ee74bd03eb001247a5638 (diff)
downloadmpv-3d4852d8e029c37902dfd83c8c7c46ba0c16cb37.tar.bz2
mpv-3d4852d8e029c37902dfd83c8c7c46ba0c16cb37.tar.xz
Adds support for LADSPA (Linux Audio Developer's Simple Plugin API) plugins.
Compilation is optional and can be controled by configure. You need to have the LADSPA SDK installed in order to have it autodetected by configure. Manual page is updated. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@14218 b3059339-0415-0410-9bf9-f77b7e298cf2
-rw-r--r--AUTHORS1
-rw-r--r--DOCS/man/en/mplayer.124
-rw-r--r--DOCS/tech/MAINTAINERS3
-rwxr-xr-xconfigure44
-rw-r--r--help/help_mp-en.h22
-rw-r--r--libaf/.cvsignore1
-rw-r--r--libaf/Makefile4
-rw-r--r--libaf/af.c4
-rw-r--r--libaf/af_ladspa.c994
9 files changed, 1095 insertions, 2 deletions
diff --git a/AUTHORS b/AUTHORS
index 6845f7ddb0..2bcc91dfec 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -611,6 +611,7 @@ van Poorten, Ivo (ivop) <ivop@euronet.nl>
* vo_jpeg suboptions parser
* vo_pnm video output driver
* vo_md5sum video output driver
+ * af_ladspa LADSPA plugin loader
Ran, Lu <hephooey@fastmail.fm>
* Chinese documentation translation
diff --git a/DOCS/man/en/mplayer.1 b/DOCS/man/en/mplayer.1
index 3e5c010dbb..a0cf9ee7af 100644
--- a/DOCS/man/en/mplayer.1
+++ b/DOCS/man/en/mplayer.1
@@ -3633,6 +3633,30 @@ difference coefficient (default: 2.5)
.B volnorm
Maximizes the volume without distorting the sound.
.
+.TP
+.B ladspa=file:label[:controls...]
+Load a LADSPA (Linux Audio Developer's Simple Plugin API) plugin.
+This filter is reentrant, so multiple LADSPA plugins can be used at once.
+.PD 0
+.RSs
+.IPs file\
+Specifies the LADSPA plugin library file.
+If LADSPA_PATH is set, it searches for the specified file.
+If it is not set, you must supply a fully specified pathname.
+.IPs label
+Specifies the filter within the library.
+Some libraries contain only one filter, but others contain many of them.
+Entering 'help' here, will list all available filters within the specified
+library, which eliminates the use of 'listplugins' from the LADSPA SDK.
+.IPs controls
+Controls are zero or more floating point values that determine the
+behaviour of the loaded plugin (for example delay, threshold or gain).
+In verbose mode (add -v to the MPlayer command line), all available controls
+and their valid ranges are printed.
+This eliminates the use of 'analyseplugin' from the LADSPA SDK.
+.RE
+.PD 1
+.
.
.
.SH "VIDEO FILTERS"
diff --git a/DOCS/tech/MAINTAINERS b/DOCS/tech/MAINTAINERS
index a301c8ac52..2530e7f9e5 100644
--- a/DOCS/tech/MAINTAINERS
+++ b/DOCS/tech/MAINTAINERS
@@ -178,3 +178,6 @@ libao2 drivers:
* ao_sgi.c - None
* ao_sun.c - None
* ao_win32.c - Sascha Sommer
+
+audio filters:
+ * af_ladspa.c - Ivo van Poorten
diff --git a/configure b/configure
index d2f7f0423f..0cb5ac0cec 100755
--- a/configure
+++ b/configure
@@ -211,6 +211,7 @@ Codecs:
--disable-internal-matroska disable internal Matroska support [enabled]
--enable-external-faad build with external FAAD2 (AAC) support [autodetect]
--disable-internal-faad disable internal FAAD2 (AAC) support [autodetect]
+ --disable-ladspa disable LADSPA plugin support [autodetect]
--disable-libdv disable libdv 0.9.5 en/decoding support [autodetect]
--disable-mad disable libmad (MPEG audio) support [autodetect]
--disable-toolame disable Toolame (MPEG layer 2 audio) support in mencoder [autodetect]
@@ -1317,6 +1318,7 @@ _matroska_internal=yes
_tremor=no
_faad_internal=auto
_faad_external=auto
+_ladspa=auto
_xmms=no
# dvdnav disabled, it does not work
#_dvdnav=no
@@ -1509,6 +1511,8 @@ for ac_option do
--disable-internal-faad) _faad_internal=no ;;
--enable-external-faad) _faad_external=yes _faad_internal=no ;;
--disable-external-faad) _faad_external=no ;;
+ --enable-ladspa) _ladspa=yes ;;
+ --disable-ladspa) _ladspa=no ;;
--enable-xmms) _xmms=yes ;;
--disable-xmms) _xmms=no ;;
--enable-dvdread) _dvdread=yes ;;
@@ -5305,6 +5309,31 @@ else
_ld_faad=
fi
+
+echocheck "LADSPA plugin support"
+if test "$_ladspa" = auto ; then
+ cat > $TMPC <<EOF
+#include <stdio.h>
+#include <ladspa.h>
+int main(void) {
+const LADSPA_Descriptor *ld = NULL;
+return 0;
+}
+EOF
+ _ladspa=no
+ cc_check && _ladspa=yes
+fi
+if test "$_ladspa" = yes; then
+ _def_ladspa="#define HAVE_LADSPA"
+ _afsrc="$_afsrc af_ladspa.c"
+ _afmodules="ladspa $_afmodules"
+else
+ _def_ladspa="#undef HAVE_LADSPA"
+ _noafmodules="ladspa $_noafmodules"
+fi
+echores "$_ladspa"
+
+
if test "$_win32" = auto ; then
if x86 ; then
qnx && _win32=no
@@ -7175,6 +7204,9 @@ $_def_faad
$_def_faad_internal
$_def_faad_version
+/* enable LADSPA plugin support */
+$_def_ladspa
+
/* enable network */
$_def_network
@@ -7307,6 +7339,16 @@ EOF
#############################################################################
+echo "Creating libaf/config.mak"
+_afobj=`echo $_afsrc | sed -e 's/\.c/\.o/g'`
+cat > libaf/config.mak << EOF
+include ../config.mak
+OPTIONAL_SRCS = $_afsrc
+OPTIONAL_OBJS = $_afobj
+EOF
+
+#############################################################################
+
cat << EOF
Config files successfully generated by ./configure !
@@ -7333,11 +7375,13 @@ cat << EOF
Codecs: $_codecmodules
Audio output: $_aomodules
Video output: $_vomodules
+ Audio filters: $_afmodules
Disabled optional drivers:
Input: $_noinputmodules
Codecs: $_nocodecmodules
Audio output: $_noaomodules
Video output: $_novomodules
+ Audio filters: $_noafmodules
'config.h' and 'config.mak' contain your configuration options.
Note: If you alter theses files (for instance CFLAGS) MPlayer may no longer
diff --git a/help/help_mp-en.h b/help/help_mp-en.h
index f0699c1369..7c6f71cf74 100644
--- a/help/help_mp-en.h
+++ b/help/help_mp-en.h
@@ -1024,3 +1024,25 @@ static char help_text[]=
// ao_plugin.c
#define MSGTR_AO_PLUGIN_InvalidPlugin "[AO PLUGIN] invalid plugin: %s\n"
+
+// ======================= AF Audio Filters ================================
+
+// libaf
+
+// af_ladspa.c
+
+#define MSGTR_AF_LADSPA_AvailableLabels "available labels in"
+#define MSGTR_AF_LADSPA_WarnNoInputs "WARNING! This LADSPA plugin has no audio inputs.\n The incoming audio signal will be lost."
+#define MSGTR_AF_LADSPA_ErrMultiChannel "Multi-channel (>2) plugins are not supported (yet).\n Use only mono and stereo plugins."
+#define MSGTR_AF_LADSPA_ErrNoOutputs "This LADSPA plugin has no audio outputs."
+#define MSGTR_AF_LADSPA_ErrInOutDiff "The number of audio inputs and audio outputs of the LADSPA plugin differ."
+#define MSGTR_AF_LADSPA_ErrFailedToLoad "failed to load"
+#define MSGTR_AF_LADSPA_ErrNoDescriptor "Couldn't find ladspa_descriptor() function in the specified library file."
+#define MSGTR_AF_LADSPA_ErrLabelNotFound "Couldn't find label in plugin library."
+#define MSGTR_AF_LADSPA_ErrNoSuboptions "No suboptions specified"
+#define MSGTR_AF_LADSPA_ErrNoLibFile "No library file specified"
+#define MSGTR_AF_LADSPA_ErrNoLabel "No filter label specified"
+#define MSGTR_AF_LADSPA_ErrNotEnoughControls "Not enough controls specified on the command line"
+#define MSGTR_AF_LADSPA_ErrControlBelow "%s: Input control #%d is below lower boundary of %0.4f.\n"
+#define MSGTR_AF_LADSPA_ErrControlAbove "%s: Input control #%d is above upper boundary of %0.4f.\n"
+
diff --git a/libaf/.cvsignore b/libaf/.cvsignore
index 4671378aef..56f8bbd451 100644
--- a/libaf/.cvsignore
+++ b/libaf/.cvsignore
@@ -1 +1,2 @@
.depend
+config.mak
diff --git a/libaf/Makefile b/libaf/Makefile
index 3dff12ced2..317547bf56 100644
--- a/libaf/Makefile
+++ b/libaf/Makefile
@@ -1,11 +1,11 @@
-include ../config.mak
+include config.mak
LIBNAME = libaf.a
SRCS=af.c af_mp.c af_dummy.c af_delay.c af_channels.c af_format.c af_resample.c \
window.c filter.c af_volume.c af_equalizer.c af_tools.c af_comp.c af_gate.c \
af_pan.c af_surround.c af_sub.c af_export.c af_volnorm.c af_extrastereo.c \
-af_lavcresample.c af_sweep.c af_hrtf.c
+af_lavcresample.c af_sweep.c af_hrtf.c $(OPTIONAL_SRCS)
OBJS=$(SRCS:.c=.o)
diff --git a/libaf/af.c b/libaf/af.c
index 98ac1169ff..ad68424492 100644
--- a/libaf/af.c
+++ b/libaf/af.c
@@ -28,6 +28,7 @@ extern af_info_t af_info_extrastereo;
extern af_info_t af_info_lavcresample;
extern af_info_t af_info_sweep;
extern af_info_t af_info_hrtf;
+extern af_info_t af_info_ladspa;
static af_info_t* filter_list[]={
&af_info_dummy,
@@ -52,6 +53,9 @@ static af_info_t* filter_list[]={
#endif
&af_info_sweep,
&af_info_hrtf,
+#ifdef HAVE_LADSPA
+ &af_info_ladspa,
+#endif
NULL
};
diff --git a/libaf/af_ladspa.c b/libaf/af_ladspa.c
new file mode 100644
index 0000000000..92fe33fb19
--- /dev/null
+++ b/libaf/af_ladspa.c
@@ -0,0 +1,994 @@
+/* ------------------------------------------------------------------------- */
+
+/*
+ * af_ladspa.c, LADSPA plugin loader
+ *
+ * Written by Ivo van Poorten <ivop@euronet.nl>
+ * Copyright (C) 2004, 2005
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ *
+ * Changelog
+ *
+ * 2004-12-23 Added to CVS
+ * 2004-12-22 Cleaned up cosmetics
+ * Made conversion loops in play() more cache-friendly
+ * 2004-12-20 Fixed bug for stereo effect on mono signal
+ * (trivial >1 to >=1 change; would segfault otherwise :-) )
+ * Removed trailing whitespace and fixed warn/err messages
+ * Have CONTROL_REINIT return a proper value
+ * 2004-12-13 More Doxygen comments
+ * 2004-12-12 Made af_ladspa optional (updated configure, af.c, etc.)
+ * 2004-12-11 Added deactivate and cleanup to uninit.
+ * Finished Doxygen comments.
+ * Moved translatable messages to help_mp-en.h
+ * 2004-12-10 Added ranges to list of controls for ease of use.
+ * Fixed sig11 bug. Implemented (dummy) outputcontrols. Some
+ * perfectly normal audio processing filters also have output
+ * controls.
+ * 2004-12-08 Added support for generators (no input, one output)
+ * Added support for stereo effects
+ * Added LADSPA_PATH support!
+ * 2004-12-07 Fixed changing buffersize. Now it's really working, also in
+ * real-time.
+ * 2004-12-06 First working version, mono-effects (1 input --> 1 output) only
+ * 2004-12-05 Started, Loading of plugin/label, Check inputs/outputs/controls
+ * Due to lack of documentation, I studied the ladspa_sdk source
+ * code and the loader code of Audacity (by Dominic Mazzoni). So,
+ * certain similarities in (small) pieces of code are not
+ * coincidental :-) No C&P jobs though!
+ *
+ */
+
+/* ------------------------------------------------------------------------- */
+
+/* Global Includes */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <unistd.h>
+#include <inttypes.h>
+#include <math.h>
+#include <limits.h>
+
+#include <dlfcn.h>
+#include <ladspa.h>
+
+/* ------------------------------------------------------------------------- */
+
+/* Local Includes */
+
+#include "af.h"
+#include "../mp_msg.h"
+#include "../help_mp.h"
+
+/* ------------------------------------------------------------------------- */
+
+/* Defines */
+
+/* why, o why, wasn't it defined like this in the first place? */
+
+#define MSGT_AF MSGT_AFILTER
+
+/* ------------------------------------------------------------------------- */
+
+/* Filter specific data */
+
+typedef struct af_ladspa_s
+{
+ int status; /**< Status of the filter.
+ * Either AF_OK or AF_ERROR
+ * Because MPlayer re-inits audio filters that
+ * _clearly_ returned AF_ERROR anyway, I use this
+ * in play() to skip the processing and return
+ * the data unchanged.
+ */
+
+ int activated; /**< 0 or 1. Activate LADSPA filters only once, even
+ * if the buffers get resized, to avoid a stuttering
+ * filter.
+ */
+
+ char *file;
+ char *label;
+
+ char *myname; /**< It's easy to have a concatenation of file and label */
+
+ void *libhandle;
+ const LADSPA_Descriptor *plugin_descriptor;
+
+ int nports;
+
+ int ninputs;
+ int *inputs;
+
+ int noutputs;
+ int *outputs;
+
+ int ninputcontrols;
+ int *inputcontrolsmap; /**< Map input port number [0-] to actual port */
+ float *inputcontrols;
+
+ int noutputcontrols;
+ int *outputcontrolsmap;
+ float *outputcontrols;
+
+ int nch; /**< number of channels */
+ int bufsize;
+ float **inbufs;
+ float **outbufs;
+ LADSPA_Handle *chhandles;
+
+} af_ladspa_t;
+
+/* ------------------------------------------------------------------------- */
+
+static int open(af_instance_t *af);
+static int af_ladspa_malloc_failed(char*);
+
+/* ------------------------------------------------------------------------- */
+
+/* Description */
+
+af_info_t af_info_ladspa = {
+ "LADSPA plugin loader",
+ "ladspa",
+ "Ivo van Poorten",
+ "",
+ AF_FLAGS_REENTRANT,
+ open
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* By lack of a better word (in my vocabulary) this is called 'parse'.
+ * Feel free to suggest an alternative.
+ */
+
+/** \brief Check for inputs, outputs and controls of a given filter.
+ *
+ * This function counts and checks all input, output and control ports
+ * of the filter that was loaded. If it turns out to be a valid
+ * filter for MPlayer use, it prints out a list of all controls and
+ * the corresponding range of its value at message level MSGL_V.
+ *
+ * \param setup Current setup of the filter. Must have its
+ * plugin_descriptor set!
+ *
+ * \return Returns AF_OK if it has a valid input/output/controls
+ * configuration. Else, it returns AF_ERROR.
+ */
+
+static int af_ladspa_parse_plugin(af_ladspa_t *setup) {
+ int p, i;
+ const LADSPA_Descriptor *pdes = setup->plugin_descriptor;
+ LADSPA_PortDescriptor d;
+ LADSPA_PortRangeHint hint;
+
+ if (!setup->libhandle)
+ return AF_ERROR; /* only call parse after a succesful load */
+ if (!setup->plugin_descriptor)
+ return AF_ERROR; /* same as above */
+
+ /* let's do it */
+
+ setup->nports = pdes->PortCount;
+
+ /* allocate memory for all inputs/outputs/controls */
+
+ setup->inputs = calloc(setup->nports, sizeof(int));
+ if (!setup->inputs) return af_ladspa_malloc_failed(setup->myname);
+
+ setup->outputs = calloc(setup->nports, sizeof(int));
+ if (!setup->outputs) return af_ladspa_malloc_failed(setup->myname);
+
+ setup->inputcontrolsmap = calloc(setup->nports, sizeof(int));
+ if (!setup->inputcontrolsmap) return af_ladspa_malloc_failed(setup->myname);
+
+ setup->inputcontrols = calloc(setup->nports, sizeof(float));
+ if (!setup->inputcontrols) return af_ladspa_malloc_failed(setup->myname);
+
+ setup->outputcontrolsmap = calloc(setup->nports, sizeof(int));
+ if (!setup->outputcontrolsmap) return af_ladspa_malloc_failed(setup->myname);
+
+ setup->outputcontrols = calloc(setup->nports, sizeof(float));
+ if (!setup->outputcontrols) return af_ladspa_malloc_failed(setup->myname);
+
+ /* set counts to zero */
+
+ setup->ninputs = 0;
+ setup->noutputs = 0;
+ setup->ninputcontrols = 0;
+ setup->noutputcontrols = 0;
+
+ /* check all ports, see what type it is and set variables according to
+ * what we have found
+ */
+
+ for (p=0; p<setup->nports; p++) {
+ d = pdes->PortDescriptors[p];
+
+ if (LADSPA_IS_PORT_AUDIO(d)) {
+ if (LADSPA_IS_PORT_INPUT(d)) {
+ setup->inputs[setup->ninputs] = p;
+ setup->ninputs++;
+ } else if (LADSPA_IS_PORT_OUTPUT(d)) {
+ setup->outputs[setup->noutputs] = p;
+ setup->noutputs++;
+ }
+ }
+
+ if (LADSPA_IS_PORT_CONTROL(d)) {
+ if (LADSPA_IS_PORT_INPUT(d)) {
+ setup->inputcontrolsmap[setup->ninputcontrols] = p;
+ setup->ninputcontrols++;
+ /* set control to zero. set values after reading the rest
+ * of the suboptions and check LADSPA_?_HINT's later.
+ */
+ setup->inputcontrols[p] = 0.0f;
+ } else if (LADSPA_IS_PORT_OUTPUT(d)); {
+ /* read and handle these too, otherwise filters that have them
+ * will sig11
+ */
+ setup->outputcontrolsmap[setup->noutputcontrols]=p;
+ setup->noutputcontrols++;
+ setup->outputcontrols[p] = 0.0f;
+ }
+ }
+
+ }
+
+ if (setup->ninputs == 0) {
+ mp_msg(MSGT_AF, MSGL_WARN, "%s: %s\n", setup->myname,
+ MSGTR_AF_LADSPA_WarnNoInputs);
+ } else if (setup->ninputs == 1) {
+ mp_msg(MSGT_AF, MSGL_V, "%s: this is a mono effect\n", setup->myname);
+ } else if (setup->ninputs == 2) {
+ mp_msg(MSGT_AF, MSGL_V, "%s: this is a stereo effect\n", setup->myname);
+ }
+
+ if (setup->ninputs > 2) {
+ mp_msg(MSGT_AF, MSGL_ERR, "%s: %s\n", setup->myname,
+ MSGTR_AF_LADSPA_ErrMultiChannel);
+ return AF_ERROR;
+ }
+
+ if (setup->noutputs == 0) {
+ mp_msg(MSGT_AF, MSGL_ERR, "%s: %s\n", setup->myname,
+ MSGTR_AF_LADSPA_ErrNoOutputs);
+ return AF_ERROR;
+ }
+
+ if (setup->noutputs != setup->ninputs ) {
+ mp_msg(MSGT_AF, MSGL_ERR, "%s: %s\n", setup->myname,
+ MSGTR_AF_LADSPA_ErrInOutDiff);
+ return AF_ERROR;
+ }
+
+ mp_msg(MSGT_AF, MSGL_V, "%s: this plugin has %d input control(s)\n",
+ setup->myname, setup->ninputcontrols);
+
+ /* Print list of controls and its range of values it accepts */
+
+ for (i=0; i<setup->ninputcontrols; i++) {
+ p = setup->inputcontrolsmap[i];
+ hint = pdes->PortRangeHints[p];
+ mp_msg(MSGT_AF, MSGL_V, " --- %d %s [", i, pdes->PortNames[p]);
+
+ if (LADSPA_IS_HINT_BOUNDED_BELOW(hint.HintDescriptor)) {
+ mp_msg(MSGT_AF, MSGL_V, "%0.2f , ", hint.LowerBound);
+ } else {
+ mp_msg(MSGT_AF, MSGL_V, "... , ");
+ }
+
+ if (LADSPA_IS_HINT_BOUNDED_ABOVE(hint.HintDescriptor)) {
+ mp_msg(MSGT_AF, MSGL_V, "%0.2f]\n", hint.UpperBound);
+ } else {
+ mp_msg(MSGT_AF, MSGL_V, "...]\n");
+ }
+
+ }
+
+ return AF_OK;
+}
+
+/* ------------------------------------------------------------------------- */
+
+/* This function might "slightly" look like dlopenLADSPA in the LADSPA SDK :-)
+ * But, I changed a few things, because imho it was broken. It did not support
+ * relative paths, only absolute paths that start with a /
+ * I think ../../some/dir/foobar.so is just as valid. And if one wants to call
+ * his library '...somename...so' he's crazy, but it should be allowed.
+ * So, search the path first, try plain *filename later.
+ * Also, try adding .so first! I like the recursion the SDK did, but it's
+ * better the other way around. -af ladspa=cmt:amp_stereo:0.5 is easier to type
+ * than -af ladspa=cmt.so:amp_stereo:0.5 :-))
+ */
+
+/** \brief dlopen() wrapper
+ *
+ * This is a wrapper around dlopen(). It tries various variations of the
+ * filename (with or without the addition of the .so extension) in various
+ * directories specified by the LADSPA_PATH environment variable. If all fails
+ * it tries the filename directly as an absolute path to the library.
+ *
+ * \param filename filename of the library to load.
+ * \param flag see dlopen(3) for a description of the flags.
+ *
+ * \return returns a pointer to the loaded library on success, or
+ * NULL if it fails to load.
+ */
+
+static void* mydlopen(const char *filename, int flag) {
+ char *buf;
+ const char *end, *start, *ladspapath;
+ int endsinso, needslash;
+ size_t filenamelen;
+ void *result = NULL;
+
+# ifdef WIN32 /* for windows there's only absolute path support.
+ * if you have a windows machine, feel free to fix
+ * this. (path separator, shared objects extension,
+ * et cetera).
+ */
+ mp_msg(MSGT_AF, MSGL_V, "\ton windows, only absolute pathnames "
+ "are supported\n");
+ mp_msg(MSGT_AF, MSGL_V, "\ttrying %s\n", filename);
+ return dlopen(filename, flag);
+# endif
+
+ filenamelen = strlen(filename);
+
+ endsinso = 0;
+ if (filenamelen > 3)
+ endsinso = (strcmp(filename+filenamelen-3, ".so") == 0);
+ if (!endsinso) {
+ buf=malloc(filenamelen+4);
+ strcpy(buf, filename);
+ strcat(buf, ".so");
+ result=mydlopen(buf, flag);
+ free(buf);
+ }
+
+ if (result)
+ return result;
+
+ ladspapath=getenv("LADSPA_PATH");
+
+ if (ladspapath) {
+
+ start=ladspapath;
+ while (*start != '\0') {
+ end=start;
+ while ( (*end != ':') && (*end != '\0') )
+ end++;
+
+ buf=malloc(filenamelen + 2 + (end-start) );
+ if (end > start)
+ strncpy(buf, start, end-start);
+ needslash=0;
+ if (end > start)
+ if (*(end-1) != '/') {
+ needslash = 1;
+ buf[end-start] = '/';
+ }
+ strcpy(buf+needslash+(end-start), filename);
+
+ mp_msg(MSGT_AF, MSGL_V, "\ttrying %s\n", buf);
+ result=dlopen(buf, flag);
+
+ free(buf);
+ if (result)
+ return result;
+
+ start = end;
+ if (*start == ':')
+ start++;
+ } /* end while there's still more in the path */
+ } /* end if there's a ladspapath */
+
+ /* last resort, just open it again, so the dlerror() message is correct */
+ mp_msg(MSGT_AF, MSGL_V, "\ttrying %s\n", filename);
+ return dlopen(filename,flag);
+}
+
+/* ------------------------------------------------------------------------- */
+
+/** \brief Load a LADSPA Plugin
+ *
+ * This function loads the LADSPA plugin specified by the file and label
+ * that are present in the setup variable. First, it loads the library.
+ * If it fails, it returns AF_ERROR. If not, it continues to look for the
+ * specified label. If it finds it, it sets the plugin_descriptor inside
+ * setup and returns AF_OK. If it doesn't, it returns AF_ERROR. Special case
+ * is a label called 'help'. In that case, it prints a list of all available
+ * labels (filters) in the library specified by file.
+ *
+ * \param setup Current setup of the filter. Contains filename and label.
+ *
+ * \return Either AF_ERROR or AF_OK, depending on the success of the operation.
+ */
+
+static int af_ladspa_load_plugin(af_ladspa_t *setup) {
+ const LADSPA_Descriptor *ladspa_descriptor;
+ LADSPA_Descriptor_Function descriptor_function;
+ int i;
+
+ /* load library */
+ mp_msg(MSGT_AF, MSGL_V, "%s: loading ladspa plugin library %s\n",
+ setup->myname, setup->file);
+
+ setup->libhandle = mydlopen(setup->file, RTLD_NOW);
+
+ if (!setup->libhandle) {
+ mp_msg(MSGT_AF, MSGL_ERR, "%s: %s %s\n\t%s\n", setup->myname,
+ MSGTR_AF_LADSPA_ErrFailedToLoad, setup->file, dlerror() );
+ return AF_ERROR;
+ }
+
+ mp_msg(MSGT_AF, MSGL_V, "%s: library found.\n", setup->myname);
+
+ /* find descriptor function */
+ dlerror();
+ descriptor_function = (LADSPA_Descriptor_Function) dlsym (setup->libhandle,
+ "ladspa_descriptor");
+
+ if (!descriptor_function) {
+ mp_msg(MSGT_AF, MSGL_ERR, "%s: %s\n\t%s\n", setup->myname,
+ MSGTR_AF_LADSPA_ErrNoDescriptor, dlerror());
+ return AF_ERROR;
+ }
+
+ /* if label == help, list all labels in library and exit */
+
+ if (strcmp(setup->label, "help") == 0) {
+ mp_msg(MSGT_AF, MSGL_INFO, "%s: %s %s:\n", setup->myname,
+ MSGTR_AF_LADSPA_AvailableLabels, setup->file);
+ for (i=0; ; i++) {
+ ladspa_descriptor = descriptor_function(i);
+ if (ladspa_descriptor == NULL) {
+ return AF_ERROR;
+ }
+ mp_msg(MSGT_AF, MSGL_INFO, " %-16s - %s (%lu)\n",
+ ladspa_descriptor->Label,
+ ladspa_descriptor->Name,
+ ladspa_descriptor->UniqueID);
+ }
+ }
+
+ mp_msg(MSGT_AF, MSGL_V, "%s: looking for label\n", setup->myname);
+
+ /* find label in library */
+ for (i=0; ; i++) {
+ ladspa_descriptor = descriptor_function(i);
+ if (ladspa_descriptor == NULL) {
+ mp_msg(MSGT_AF, MSGL_ERR, "%s: %s\n", setup->myname,
+ MSGTR_AF_LADSPA_ErrLabelNotFound);
+ return AF_ERROR;
+ }
+ if (strcmp(ladspa_descriptor->Label, setup->label) == 0) {
+ setup->plugin_descriptor = ladspa_descriptor;
+ mp_msg(MSGT_AF, MSGL_V, "%s: %s found\n", setup->myname,
+ setup->label);
+ return AF_OK;
+ }
+ }
+
+ return AF_OK;
+}
+
+/* ------------------------------------------------------------------------- */
+
+/** \brief Print a malloc() failed error message.
+ *
+ * Generic function which can be called if a call to malloc(), calloc(),
+ * strdup(), et cetera, failed. It prints a message to the console and
+ * returns AF_ERROR.
+ *
+ * \return AF_ERROR
+ */
+
+static int af_ladspa_malloc_failed(char *myname) {
+ mp_msg(MSGT_AF, MSGL_ERR, "%s: %s\n", myname, MSGTR_MemAllocFailed);
+ return AF_ERROR;
+}
+
+/* ------------------------------------------------------------------------- */
+
+/** \brief Controls the filter.
+ *
+ * Control the behaviour of the filter.
+ *
+ * Commands:
+ * CONTROL_REINIT Sets the af structure with proper values for number
+ * of channels, rate, format, et cetera.
+ * CONTROL_COMMAND_LINE Parses the suboptions given to this filter
+ * through arg. It first parses the filename and
+ * the label. After that, it loads the filter
+ * and finds out its proprties. Then in continues
+ * parsing the controls given on the commandline,
+ * if any are needed.
+ *
+ * \param af Audio filter instance
+ * \param cmd The command to execute
+ * \param arg Arguments to the command
+ *
+ * \return Either AF_ERROR or AF_OK, depending on the succes of the
+ * operation.
+ */
+
+static int control(struct af_instance_s *af, int cmd, void *arg) {
+ af_ladspa_t *setup = (af_ladspa_t*) af->setup;
+ int i, r;
+ float val;
+
+ switch(cmd) {
+ case AF_CONTROL_REINIT:
+ mp_msg(MSGT_AFILTER, MSGL_V, "%s: (re)init\n", setup->myname);
+
+ if (!arg) return AF_ERROR;
+
+ /* for now, only accept 16 bit signed int */
+
+ af->data->rate = ((af_data_t*)arg)->rate;
+ af->data->nch = ((af_data_t*)arg)->nch;
+ af->data->format = AF_FORMAT_SI | AF_FORMAT_NE;
+ af->data->bps = 2;
+
+ /* arg->len is not set here yet, so init of buffers and connecting the
+ * filter, has to be done in play() :-/
+ */
+
+ return af_test_output(af, (af_data_t*)arg);
+ case AF_CONTROL_COMMAND_LINE: {
+ char *buf;
+
+ mp_msg(MSGT_AFILTER, MSGL_V, "%s: parse suboptions\n", setup->myname);
+
+ /* suboption parser here!
+ * format is (ladspa=)file:label:controls....
+ */
+
+ if (!arg) {
+ mp_msg(MSGT_AF, MSGL_ERR, "%s: %s\n", setup->myname,
+ MSGTR_AF_LADSPA_ErrNoSuboptions);
+ return AF_ERROR;
+ }
+
+ buf = malloc(strlen(arg)+1);
+ if (!buf) return af_ladspa_malloc_failed(setup->myname);
+
+ /* file... */
+ buf[0] = '\0';
+ sscanf(arg, "%[^:]", buf);
+ if (buf[0] == '\0') {
+ mp_msg(MSGT_AF, MSGL_ERR, "%s: %s\n", setup->myname,
+ MSGTR_AF_LADSPA_ErrNoLibFile);
+ free(buf);
+ return AF_ERROR;
+ }
+ arg += strlen(buf);
+ setup->file = strdup(buf);
+ if (!setup->file) return af_ladspa_malloc_failed(setup->myname);
+ mp_msg(MSGT_AF, MSGL_V, "%s: file --> %s\n", setup->myname,
+ setup->file);
+ if (*(char*)arg != '\0') arg++; /* read ':' */
+
+ /* label... */
+ buf[0] = '\0';
+ sscanf(arg, "%[^:]", buf);
+ if (buf[0] == '\0') {
+ mp_msg(MSGT_AF, MSGL_ERR, "%s: %s\n", setup->myname,
+ MSGTR_AF_LADSPA_ErrNoLabel);
+ free(buf);
+ return AF_ERROR;
+ }
+ arg += strlen(buf);
+ setup->label = strdup(buf);
+ if (!setup->label) return af_ladspa_malloc_failed(setup->myname);
+ mp_msg(MSGT_AF, MSGL_V, "%s: label --> %s\n", setup->myname,
+ setup->label);
+/* if (*(char*)arg != '0') arg++; */ /* read ':' */
+
+ free(buf); /* no longer needed */
+
+ /* set new setup->myname */
+
+ if(setup->myname) free(setup->myname);
+ setup->myname = calloc(strlen(af_info_ladspa.name)+strlen(setup->file)+
+ strlen(setup->label)+6, 1);
+ snprintf(setup->myname, strlen(af_info_ladspa.name)+
+ strlen(setup->file)+strlen(setup->label)+6, "%s: (%s:%s)",
+ af_info_ladspa.name, setup->file, setup->label);
+
+ /* load plugin :) */
+
+ if ( af_ladspa_load_plugin(setup) != AF_OK )
+ return AF_ERROR;
+
+ /* see what inputs, outputs and controls this plugin has */
+ if ( af_ladspa_parse_plugin(setup) != AF_OK )
+ return AF_ERROR;
+
+ /* ninputcontrols is set by now, read control values from arg */
+
+ for(i=0; i<setup->ninputcontrols; i++) {
+ if (!arg || (*(char*)arg != ':') ) {
+ mp_msg(MSGT_AF, MSGL_ERR, "%s: %s\n", setup->myname,
+ MSGTR_AF_LADSPA_ErrNotEnoughControls);
+ return AF_ERROR;
+ }
+ arg++;
+ r = sscanf(arg, "%f", &val);
+ if (r!=1) {
+ mp_msg(MSGT_AF, MSGL_ERR, "%s: %s\n", setup->myname,
+ MSGTR_AF_LADSPA_ErrNotEnoughControls);
+ return AF_ERROR;
+ }
+ setup->inputcontrols[setup->inputcontrolsmap[i]] = val;
+ arg = strchr(arg, ':');
+ }
+
+ mp_msg(MSGT_AF, MSGL_V, "%s: input controls: ", setup->myname);
+ for(i=0; i<setup->ninputcontrols; i++) {
+ mp_msg(MSGT_AF, MSGL_V, "%0.4f ",
+ setup->inputcontrols[setup->inputcontrolsmap[i]]);
+ }
+ mp_msg(MSGT_AF, MSGL_V, "\n");
+
+ /* check boundaries of inputcontrols */
+
+ mp_msg(MSGT_AF, MSGL_V, "%s: checking boundaries of input controls\n",
+ setup->myname);
+ for(i=0; i<setup->ninputcontrols; i++) {
+ int p = setup->inputcontrolsmap[i];
+ LADSPA_PortRangeHint hint =
+ setup->plugin_descriptor->PortRangeHints[p];
+ val = setup->inputcontrols[p];
+
+ if (LADSPA_IS_HINT_BOUNDED_BELOW(hint.HintDescriptor) &&
+ val < hint.LowerBound) {
+ mp_msg(MSGT_AF, MSGL_ERR, MSGTR_AF_LADSPA_ErrControlBelow,
+ setup->myname, i, hint.LowerBound);
+ return AF_ERROR;
+ }
+ if (LADSPA_IS_HINT_BOUNDED_ABOVE(hint.HintDescriptor) &&
+ val > hint.UpperBound) {