diff options
-rw-r--r-- | AUTHORS | 1 | ||||
-rw-r--r-- | DOCS/man/en/mplayer.1 | 24 | ||||
-rw-r--r-- | DOCS/tech/MAINTAINERS | 3 | ||||
-rwxr-xr-x | configure | 44 | ||||
-rw-r--r-- | help/help_mp-en.h | 22 | ||||
-rw-r--r-- | libaf/.cvsignore | 1 | ||||
-rw-r--r-- | libaf/Makefile | 4 | ||||
-rw-r--r-- | libaf/af.c | 4 | ||||
-rw-r--r-- | libaf/af_ladspa.c | 994 |
9 files changed, 1095 insertions, 2 deletions
@@ -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 @@ -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); + |