summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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) {
+ mp_msg(MSGT_AF, MSGL_ERR, MSGTR_AF_LADSPA_ErrControlAbove,
+ setup->myname, i, hint.UpperBound);
+ return AF_ERROR;
+ }
+ }
+ mp_msg(MSGT_AF, MSGL_V, "%s: all controls have sane values\n",
+ setup->myname);
+
+ /* All is well! */
+ setup->status = AF_OK;
+
+ return AF_OK; }
+ }
+
+ return AF_UNKNOWN;
+}
+
+/* ------------------------------------------------------------------------- */
+
+/** \brief Uninitialise the LADSPA Plugin Loader filter.
+ *
+ * This function deactivates the plugin(s), cleans up, frees all allocated
+ * memory and exits.
+ *
+ * \return No return value.
+ */
+
+static void uninit(struct af_instance_s *af) {
+ int i;
+
+ if (af->data)
+ free(af->data);
+ if (af->setup) {
+ af_ladspa_t *setup = (af_ladspa_t*) af->setup;
+ const LADSPA_Descriptor *pdes