ref: f40e25a18472c0afc300c13d8363085cbec05b64
dir: /src/ladspa.c/
/* * LADSPA effect support for sox * (c) Reuben Thomas <rrt@sc3d.org> 2007 * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, write to the Free Software * Foundation, Fifth Floor, 51 Franklin Street, Boston, MA 02111-1301, * USA. */ #include "sox_i.h" #ifdef HAVE_LADSPA_H #include <assert.h> #include <limits.h> #include <string.h> #include <math.h> #include <ltdl.h> #include <ladspa.h> static sox_effect_handler_t sox_ladspa_effect; /* Private data for resampling */ typedef struct { char *name; /* plugin name */ lt_dlhandle lth; /* dynamic object handle */ const LADSPA_Descriptor *desc; /* plugin descriptor */ LADSPA_Handle handle; /* instantiated plugin handle */ LADSPA_Data *control; /* control ports */ unsigned long input_port, output_port; } *ladspa_t; static LADSPA_Data ladspa_default(const LADSPA_PortRangeHint *p) { LADSPA_Data d; if (LADSPA_IS_HINT_DEFAULT_0(p->HintDescriptor)) d = 0.0; else if (LADSPA_IS_HINT_DEFAULT_1(p->HintDescriptor)) d = 1.0; else if (LADSPA_IS_HINT_DEFAULT_100(p->HintDescriptor)) d = 100.0; else if (LADSPA_IS_HINT_DEFAULT_440(p->HintDescriptor)) d = 440.0; else if (LADSPA_IS_HINT_DEFAULT_MINIMUM(p->HintDescriptor)) d = p->LowerBound; else if (LADSPA_IS_HINT_DEFAULT_MAXIMUM(p->HintDescriptor)) d = p->UpperBound; else if (LADSPA_IS_HINT_DEFAULT_LOW(p->HintDescriptor)) { if (LADSPA_IS_HINT_LOGARITHMIC(p->HintDescriptor)) d = exp(log(p->LowerBound) * 0.75 + log(p->UpperBound) * 0.25); else d = p->LowerBound * 0.75 + p->UpperBound * 0.25; } else if (LADSPA_IS_HINT_DEFAULT_MIDDLE(p->HintDescriptor)) { if (LADSPA_IS_HINT_LOGARITHMIC(p->HintDescriptor)) d = exp(log(p->LowerBound) * 0.5 + log(p->UpperBound) * 0.5); else d = p->LowerBound * 0.5 + p->UpperBound * 0.5; } else if (LADSPA_IS_HINT_DEFAULT_HIGH(p->HintDescriptor)) { if (LADSPA_IS_HINT_LOGARITHMIC(p->HintDescriptor)) d = exp(log(p->LowerBound) * 0.25 + log(p->UpperBound) * 0.75); else d = p->LowerBound * 0.25 + p->UpperBound * 0.75; } else { /* shouldn't happen */ /* FIXME: Deal with this at a higher level */ sox_fail("non-existent default value; using 0.1"); d = 0.1; /* Should at least avoid divide by 0 */ } return d; } /* * Process options */ static int sox_ladspa_getopts(sox_effect_t *effp, int n, char **argv) { ladspa_t l_st = (ladspa_t)effp->priv; char *path; union {LADSPA_Descriptor_Function fn; lt_ptr ptr;} ltptr; unsigned long index = 0, i; double arg; l_st->input_port = ULONG_MAX; l_st->output_port = ULONG_MAX; /* Get module name */ if (n >= 1) { l_st->name = argv[0]; n--; argv++; } /* Load module */ path = getenv("LADSPA_PATH"); if (path == NULL) path = LADSPA_PATH; lt_dlsetsearchpath(path); if ((l_st->lth = lt_dlopenext(l_st->name)) == NULL) { sox_fail("could not open LADSPA plugin %s", l_st->name); return SOX_EOF; } /* Get descriptor function */ if ((ltptr.ptr = lt_dlsym(l_st->lth, "ladspa_descriptor")) == NULL) { sox_fail("could not find ladspa_descriptor"); return SOX_EOF; } /* If no plugins in this module, complain */ if (ltptr.fn(0) == NULL) { sox_fail("no plugins found"); return SOX_EOF; } /* Get first plugin descriptor */ l_st->desc = ltptr.fn(0); assert(l_st->desc); /* We already know this will work */ /* If more than one plugin, or first argument is not a number, try to use first argument as plugin label. */ if (n > 0 && (ltptr.fn(1) != NULL || !sscanf(argv[0], "%lf", &arg))) { while (l_st->desc && strcmp(l_st->desc->Label, argv[0]) != 0) l_st->desc = ltptr.fn(++index); if (l_st->desc == NULL) { sox_fail("no plugin called `%s' found", argv[0]); return SOX_EOF; } else n--; argv++; } /* Scan the ports to check there's one input and one output */ l_st->control = xcalloc(l_st->desc->PortCount, sizeof(LADSPA_Data)); for (i = 0; i < l_st->desc->PortCount; i++) { const LADSPA_PortDescriptor port = l_st->desc->PortDescriptors[i]; /* Check port is well specified. All control ports should be inputs, but don't bother checking, as we never rely on this. */ if (LADSPA_IS_PORT_INPUT(port) && LADSPA_IS_PORT_OUTPUT(port)) { sox_fail("port %lu is both input and output", i); return SOX_EOF; } else if (LADSPA_IS_PORT_CONTROL(port) && LADSPA_IS_PORT_AUDIO(port)) { sox_fail("port %lu is both audio and control", i); return SOX_EOF; } if (LADSPA_IS_PORT_AUDIO(port)) { if (LADSPA_IS_PORT_INPUT(port)) { if (l_st->input_port != ULONG_MAX) { sox_fail("can't use a plugin with more than one audio input port"); return SOX_EOF; } l_st->input_port = i; } else if (LADSPA_IS_PORT_OUTPUT(port)) { if (l_st->output_port != ULONG_MAX) { sox_fail("can't use a plugin with more than one audio output port"); return SOX_EOF; } l_st->output_port = i; } } else { /* Control port */ if (n == 0) { if (!LADSPA_IS_HINT_HAS_DEFAULT(l_st->desc->PortRangeHints[i].HintDescriptor)) { sox_fail("not enough arguments for control ports"); return SOX_EOF; } l_st->control[i] = ladspa_default(&(l_st->desc->PortRangeHints[i])); sox_debug("default argument for port %lu is %f", i, l_st->control[i]); } else { if (!sscanf(argv[0], "%lf", &arg)) return sox_usage(effp); l_st->control[i] = (LADSPA_Data)arg; sox_debug("argument for port %lu is %f", i, l_st->control[i]); n--; argv++; } } } /* Stop if we have any unused arguments */ return n? sox_usage(effp) : SOX_SUCCESS; } /* * Prepare processing. */ static int sox_ladspa_start(sox_effect_t * effp) { ladspa_t l_st = (ladspa_t)effp->priv; unsigned long i; /* Instantiate the plugin */ sox_debug("rate for plugin is %g", effp->in_signal.rate); l_st->handle = l_st->desc->instantiate(l_st->desc, (unsigned long)effp->in_signal.rate); if (l_st->handle == NULL) { sox_fail("could not instantiate plugin"); return SOX_EOF; } for (i = 0; i < l_st->desc->PortCount; i++) { const LADSPA_PortDescriptor port = l_st->desc->PortDescriptors[i]; if (LADSPA_IS_PORT_CONTROL(port)) l_st->desc->connect_port(l_st->handle, i, &(l_st->control[i])); } /* If needed, activate the plugin */ if (l_st->desc->activate) l_st->desc->activate(l_st->handle); return SOX_SUCCESS; } /* * Process one bufferful of data. */ static int sox_ladspa_flow(sox_effect_t * effp, const sox_sample_t *ibuf, sox_sample_t *obuf, sox_size_t *isamp, sox_size_t *osamp) { ladspa_t l_st = (ladspa_t)effp->priv; sox_size_t i, len = min(*isamp, *osamp); *osamp = *isamp = len; if (len) { LADSPA_Data *buf = xmalloc(sizeof(LADSPA_Data) * len); /* Insert input if effect takes it */ if (l_st->input_port != ULONG_MAX) { /* Copy the input; FIXME: Assume LADSPA_Data == float! */ for (i = 0; i < len; i++) buf[i] = SOX_SAMPLE_TO_FLOAT_32BIT(ibuf[i], effp->clips); /* Connect the input port */ l_st->desc->connect_port(l_st->handle, l_st->input_port, buf); } /* Connect the output port if used */ if (l_st->output_port != ULONG_MAX) l_st->desc->connect_port(l_st->handle, l_st->output_port, buf); /* Run the plugin */ l_st->desc->run(l_st->handle, len); /* Grab output if effect produces it */ if (l_st->output_port != ULONG_MAX) /* FIXME: Assume LADSPA_Data == float! */ for (i = 0; i < len; i++) { obuf[i] = SOX_FLOAT_32BIT_TO_SAMPLE(buf[i], effp->clips); } free(buf); } return SOX_SUCCESS; } /* * Nothing to do. */ static int sox_ladspa_drain(sox_effect_t * effp UNUSED, sox_sample_t *obuf UNUSED, sox_size_t *osamp) { *osamp = 0; return SOX_SUCCESS; } /* * Do anything required when you stop reading samples. * Don't close input file! */ static int sox_ladspa_stop(sox_effect_t * effp) { ladspa_t l_st = (ladspa_t)effp->priv; /* If needed, deactivate the plugin */ if (l_st->desc->deactivate) l_st->desc->deactivate(l_st->handle); return SOX_SUCCESS; } static sox_effect_handler_t sox_ladspa_effect = { "ladspa", "MODULE [PLUGIN] [ARGUMENT...]", 0, sox_ladspa_getopts, sox_ladspa_start, sox_ladspa_flow, sox_ladspa_drain, sox_ladspa_stop, NULL }; const sox_effect_handler_t *sox_ladspa_effect_fn(void) { return &sox_ladspa_effect; } #endif /* HAVE_LADSPA */