shithub: sox

Download patch

ref: 678e9696e73fcc7502fa443364ce1d8f88cb0944
parent: 471e530902d84fc3c4d21777fbe42af185f3a586
author: rrt <rrt>
date: Sun May 27 15:10:38 EDT 2007

First cut of working LADSPA code, some cleanup.

--- a/configure.ac
+++ b/configure.ac
@@ -243,21 +243,6 @@
 AM_CONDITIONAL(HAVE_MP3, test x$using_mad = xyes -o x$using_lame = xyes)
 AC_SUBST(MP3_LIBS)
 
-dnl Test for libsamplerate.
-AC_ARG_WITH(samplerate,
-    AC_HELP_STRING([--without-samplerate],
-        [Don't try to use libsamplerate (aka Secret Rabbit Code)]),
-        [with_samplerate=$withval])
-if test "$with_samplerate" != "no"; then
-    using_samplerate=yes
-    SOX_PATH_SAMPLERATE(, using_samplerate=no)
-    if test "$with_samplerate" = "yes" -a "$using_samplerate" = "no"; then
-        AC_MSG_FAILURE([cannot find libsamplerate])
-    fi
-fi
-AC_SUBST(SAMPLERATE_CFLAGS)
-AC_SUBST(SAMPLERATE_LIBS)
-
 dnl Check for amr-wb libraries
 AC_ARG_WITH(amr-wb,
     AC_HELP_STRING([--without-amr-wb],
@@ -292,6 +277,36 @@
 AM_CONDITIONAL(HAVE_AMR_NB, test x$using_amr_nb = xyes)
 AC_SUBST(AMR_NB_LIBS)
 
+dnl Test for libsamplerate.
+AC_ARG_WITH(samplerate,
+    AC_HELP_STRING([--without-samplerate],
+        [Don't try to use libsamplerate (aka Secret Rabbit Code)]),
+        [with_samplerate=$withval])
+if test "$with_samplerate" != "no"; then
+    using_samplerate=yes
+    SOX_PATH_SAMPLERATE(, using_samplerate=no)
+    if test "$with_samplerate" = "yes" -a "$using_samplerate" = "no"; then
+        AC_MSG_FAILURE([cannot find libsamplerate])
+    fi
+fi
+AC_SUBST(SAMPLERATE_CFLAGS)
+AC_SUBST(SAMPLERATE_LIBS)
+
+dnl Test for LADSPA
+AC_ARG_WITH(ladspa,
+    AC_HELP_STRING([--without-ladspa], [Don't try to use LADSPA]), [with_ladspa=$withval])
+if test "$with_ladspa" != "no"; then
+    using_ladspa=yes
+    AC_CHECK_HEADERS(ladspa.h,, using_ladspa=no)
+    if test "$with_ladspa" = "yes" -a "$using_ladspa" = "no"; then
+        AC_MSG_FAILURE([cannot find ladspa.h])
+    fi
+fi
+LADSPA_PATH=${libdir}/ladspa
+AC_ARG_WITH(ladspa-path,
+    AC_HELP_STRING([--with-ladspa-path], [Default search path for LADSPA plugins]), [LADSPA_PATH=$withval])
+AC_SUBST(LADSPA_PATH)
+
 dnl Generate output files.
 AX_CREATE_STDINT_H(src/soxstdint.h)
 AC_CONFIG_FILES(Makefile src/Makefile src/libgsm/Makefile lpc10/Makefile)
@@ -318,9 +333,10 @@
 echo "ffmpeg formats.................... $with_ffmpeg"
 echo "MAD MP3 reader.................... $using_mad"
 echo "LAME MP3 writer................... $using_lame"
-echo "Secret Rabbit Code resampling..... $using_samplerate"
 echo "AMR-WB format..................... $using_amr_wb"
 echo "AMR-NB format..................... $using_amr_nb"
+echo "LADSPA effects.................... $using_ladspa"
+echo "Secret Rabbit Code resampling..... $using_samplerate"
 echo
 echo "Configure finished.  Do 'make && make install' to compile and install SoX."
 echo
--- a/libsox.3
+++ b/libsox.3
@@ -36,12 +36,14 @@
 .P
 .B int sox_seek(ft_t \fIft\fB, sox_size_t \fIoffset\fB, int \fIwhence\fB);
 .P
-.B sox_effect_t *sox_geteffect(const char *\fIeffect_name\fB);
+.B sox_effect_handler_t const *sox_find_effect(char const *\fIname\fB);
 .P
-.B int sox_geteffect_opts(int \fIargc\fB, char **\fIargv\fB);
+.B void sox_create_effect(sox_effect_t \fIeffp\fB, sox_effect_handler_t const *\fIe\fB);
 .P
-.B int sox_updateeffect(eff_t \fIeffp\fB, const sox_signalinfo_t *\fIin\fB, const sox_signalinfo_t *\fIout\fB, int \fIeffect_mask\fB);
+int sox_get_effect(sox_effect_t \fIeffp\fB, const char *\fIname\fB);
 .P
+.B int sox_update_effect(eff_t \fIeffp\fB, const sox_signalinfo_t *\fIin\fB, const sox_signalinfo_t *\fIout\fB, int \fIeffect_mask\fB);
+.P
 .B cc \fIfile.c\fB -o \fIfile \f-lsox
 .fi
 .SH DESCRIPTION
@@ -91,18 +93,19 @@
 underlying file or set of functions. If the format handler was being
 used for output, any buffered data is written first.
 .P
-The function \fBsox_geteffect\fR finds effect \fIname\fR, returning a
-pointer to its \fIsox_effect_t\fR if it exists, and NULL otherwise.
+The function \fBsox_find_effect\fR finds effect \fIname\fR, returning
+a pointer to its \fIsox_effect_handler_t\fR if it exists, and NULL
+otherwise.
 .P
-The function \fBsox_geteffect_opts\fR finds the extent of options for
-the current effect, by examining each argument until it finds another
-effect name or runs out of arguments. It returns the number of
-arguments for the current effect.
+The function \fBsox_create_effect\fR instantiates an effect into a
+\fIsox_effect_t\fR given a \fIsox_effect_handler_t *\fR. Any missing
+methods are automatically set to the corresponding \fBnothing\fR
+method.
 .P
-The \fBsox_updateeffect\fR function copies input and output signal
+The \fBsox_update_effect\fR function copies input and output signal
 info into effect structures. The \fIeffect_mask\fR parameter is the
 return value of the previous call to this function; for the first
-call, pass 0.
+call, pass 0. The function returns the updated effect mask.
 .P
 SoX includes skeleton C files to assist you in writing new
 formats (skelform.c) and effects (skeleff.c). sox.c itself is a good
@@ -111,14 +114,14 @@
 writing.
 .SH RETURN VALUE
 Upon successful completion \fBsox_open_input\fR and
-\fBsox_open_output\fR return a ft_t (which is a pointer). Otherwise,
-NULL is returned. TODO: Need a what to return reason for failures.
-Currently, relies on sox_warn to print information.
+\fBsox_open_output\fR return an \fIft_t\fR (which is a pointer).
+Otherwise, NULL is returned. TODO: Need a way to return reason for
+failures. Currently, relies on \fBsox_warn\fR to print information.
 .P
 \fBsox_read\fR and \fBsox_write\fR return the number of samples
 successfully read or written. If an error occurs, or the end-of-file
 is reached, the return value is a short item count or SOX_EOF. TODO:
-\fBsox_read\fR does not distiguish between end-of-ifle and error. Need
+\fBsox_read\fR does not distiguish between end-of-file and error. Need
 an feof() and ferror() concept to determine which occured.
 .P
 Upon successful completion \fBsox_close\fR returns 0. Otherwise, SOX_EOF
--- a/soxeffect.7
+++ b/soxeffect.7
@@ -412,6 +412,16 @@
 .SP
 See also \fBfilter\fR for filters with a steeper roll-off.
 .TP
+\fBladspa\fR \fBmodule\fR [\fBplugin\fR] [\fBargument\fR...]
+Apply a LADSPA [5] (Linux Audio Developer's Simple Plugin API) plugin.
+Despite the name, LADSPA is not Linux-specific, and a wide range of
+effects is available as LADSPA plugins, such as cmt [6] (the Computer
+Music Toolkit) and Steve Harris's plugin collection [7]. The first
+argument is the plugin module, the second the name of the plugin (a
+module can contain more than one plugin) and any other arguments are
+for the control ports of the plugin. Only plugins with precisely one
+input and one output port can be used at present.
+.TP
 \fBlowpass\fR [\fB-1\fR|\fB-2\fR] \fIfrequency\fR [\fRwidth\fR[\fBq\fR\^|\^\fBo\fR\^|\^\fBh\fR]]
 Apply a low-pass filter.
 See the description of the \fBhighpass\fR effect for details.
@@ -715,14 +725,15 @@
 the beta for a Kaiser window.  Beta \(<= 2 selects a Nuttall window.
 If unspecified, the default is a Kaiser window with beta 16.
 .SP
-In the case of Kaiser window (beta > 2), lower betas produce a somewhat
-faster transition from pass-band to stop-band, at the cost of noticeable artifacts.
-A beta of 16 is the default, beta less than 10 is not recommended.  If you want
-a sharper cutoff, don't use low beta's, use a longer sample window.
-A Nuttall window is selected by specifying any `beta' \(<= 2, and the
-Nuttall window has somewhat steeper cutoff than the default Kaiser window.
-You will probably not need to use the beta parameter at all, unless you are
-just curious about comparing the effects of Nuttall vs. Kaiser windows.
+In the case of Kaiser window (beta > 2), lower betas produce a
+somewhat faster transition from pass-band to stop-band, at the cost of
+noticeable artifacts. A beta of 16 is the default, beta less than 10
+is not recommended. If you want a sharper cutoff, don't use low
+beta's, use a longer sample window. A Nuttall window is selected by
+specifying any `beta' \(<= 2, and the Nuttall window has somewhat
+steeper cutoff than the default Kaiser window. You will probably not
+need to use the beta parameter at all, unless you are just curious
+about comparing the effects of Nuttall vs. Kaiser windows.
 .SP
 This is the default effect if the two files have different sampling rates.
 Default parameters are, as indicated above, Kaiser window of length 45,
@@ -755,24 +766,30 @@
 Reverse the audio completely.
 Requires disk space to store the data to be reversed.
 .TP
-\fBsilence \fR[\fB\-l\fR] \fIabove-periods\fR [\fIduration threshold\fR[\fBd\fR\^|\^\fB%\fR] [\fIbelow-periods duration threshold\fR[\fBd\fR\^|\^\fB%\fR]]
+\fBsilence \fR[\fB\-l\fR] \fIabove-periods\fR [\fIduration
+threshold\fR[\fBd\fR\^|\^\fB%\fR] [\fIbelow-periods duration
+threshold\fR[\fBd\fR\^|\^\fB%\fR]]
 .SP
-Removes silence from the beginning, middle, or end of the audio.  Silence is anything below a specified threshold.
+Removes silence from the beginning, middle, or end of the audio.
+Silence is anything below a specified threshold.
 .SP
-The \fIabove-periods\fR value is used to indicate if audio should be trimmed at
-the beginning of the audio.  A value of zero indicates no silence
-should be trimmed from the beginning.  When specifying an non-zero
-\fIabove-periods\fR, it trims audio up until it finds non-silence.
-Normally, when trimming silence from
-beginning of audio the \fIabove-periods\fR will be 1 but it can be increased to
-higher values to trim all audio up to a specific count of non-silence periods.
-For example, if you had an audio file with two songs that each contained
-2 seconds of silence before the song, you could specify an \fIabove-period\fR
-of 2 to strip out both silence periods and the first song.
+The \fIabove-periods\fR value is used to indicate if audio should be
+trimmed at the beginning of the audio. A value of zero indicates no
+silence should be trimmed from the beginning. When specifying an
+non-zero \fIabove-periods\fR, it trims audio up until it finds
+non-silence. Normally, when trimming silence from beginning of audio
+the \fIabove-periods\fR will be 1 but it can be increased to higher
+values to trim all audio up to a specific count of non-silence
+periods. For example, if you had an audio file with two songs that
+each contained 2 seconds of silence before the song, you could specify
+an \fIabove-period\fR of 2 to strip out both silence periods and the
+first song.
 .SP
-When \fIabove-periods\fR is non-zero, you must also specify a \fIduration\fR and
-\fIthreshold\fR.  \fIDuration\fR indications the amount of time that non-silence must be
-detected before it stops trimming audio.  By increasing the duration, burst of noise can be treated as silence and trimmed off.
+When \fIabove-periods\fR is non-zero, you must also specify a
+\fIduration\fR and \fIthreshold\fR. \fIDuration\fR indications the
+amount of time that non-silence must be detected before it stops
+trimming audio. By increasing the duration, burst of noise can be
+treated as silence and trimmed off.
 .SP
 \fIThreshold\fR is used to indicate what sample value you should treat as
 silence.  For digital audio, a value of 0 may be fine but for audio
@@ -816,11 +833,14 @@
 For example, if you want to remove long pauses between words
 but do not want to remove the pauses completely.
 .SP
-The \fIperiod\fR counts are in units of samples.  \fIDuration\fR counts may be in the format of hh:mm:ss.frac, or the exact count of samples.  \fIThreshold\fR numbers may be suffixed with
+The \fIperiod\fR counts are in units of samples. \fIDuration\fR counts
+may be in the format of hh:mm:ss.frac, or the exact count of samples.
+\fIThreshold\fR numbers may be suffixed with
 .B d
 to indicate the value is in decibels, or
 .B %
-to indicate a percentage of maximum value of the sample value (\fB0%\fR specifies pure digital silence).
+to indicate a percentage of maximum value of the sample value
+(\fB0%\fR specifies pure digital silence).
 .TP
 \fBspeed \fIfactor\fR[\fBc\fR]
 Adjust the audio speed (pitch and tempo together).  \fIfactor\fR
@@ -1178,6 +1198,21 @@
 Wikipedia,
 .IR "Decibel" ,
 http://en.wikipedia.org/wiki/Decibel
+.TP
+[5]
+Richard Furse,
+.IR "Linux Audio Developer's Simple Plugin API" ,
+http://www.ladspa.org/
+.TP
+[6]
+Richard Furse,
+.IR "Computer Music Toolkit" ,
+http://www.ladspa.org/cmt
+.TP
+[7]
+Steve Harris,
+.IR "LADSPA plugins" ,
+http://plugin.org.uk/
 .SH AUTHORS
 Chris Bagwell (cbagwell@users.sourceforge.net).
 Other authors and contributors are listed in the AUTHORS file that
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -10,7 +10,7 @@
 LIBTOOL = @LIBTOOL@ @LIBTOOLFLAGS@
 
 bin_PROGRAMS = sox
-lib_LTLIBRARIES = libsox.la
+lib_LTLIBRARIES = libsox.la libsfx.la
 include_HEADERS = sox.h soxstdint.h
 
 # Format modules; listed first so optional ones can be added later
@@ -78,7 +78,7 @@
 libsox_fmt_dat_la_SOURCES = dat.c
 libsox_fmt_dat_la_LIBADD = libsox.la
 libsox_fmt_gsm_la_SOURCES = gsm.c
-libsox_fmt_gsm_la_LIBADD = libsox.la @GSM_LIBS@
+libsox_fmt_gsm_la_LIBADD = libsox.la @GSM_LIBS@ @LIBGSM_LIBADD@
 libsox_fmt_hcom_la_SOURCES = hcom.c
 libsox_fmt_hcom_la_LIBADD = libsox.la
 libsox_fmt_lpc10_la_SOURCES = lpc10.c
@@ -104,7 +104,7 @@
 libsox_fmt_ima_la_SOURCES = ima-fmt.c
 libsox_fmt_ima_la_LIBADD = libsox.la
 libsox_fmt_wav_la_SOURCES = adpcm.c adpcm.h ima_rw.c ima_rw.h wav.c wav.h
-libsox_fmt_wav_la_LIBADD = libsox.la @GSM_LIBS@
+libsox_fmt_wav_la_LIBADD = libsox.la @GSM_LIBS@ @LIBGSM_LIBADD@
 libsox_fmt_wve_la_SOURCES = wve.c
 libsox_fmt_wve_la_LIBADD = libsox.la
 libsox_fmt_xa_la_SOURCES = xa.c
@@ -170,7 +170,7 @@
 pkglib_LTLIBRARIES += libsox_fmt_sunau.la
 endif
 
-effects = band.h biquad.c biquad.h biquads.c chorus.c compand.c	\
+libsfx_la_SOURCES = band.h biquad.c biquad.h biquads.c chorus.c compand.c	\
 	  compandt.c compandt.h dcshift.c dither.c earwax.c echo.c echos.c	\
 	  fade.c FFT.c FFT.h filter.c flanger.c ladspa.c mcompand.c	\
 	  mixer.c noiseprof.c noisered.c noisered.h pad.c pan.c		\
@@ -178,17 +178,17 @@
 	  resample.c reverb.c reverse.c silence.c skeleff.c speed.c	\
 	  stat.c stretch.c swap.c synth.c tremolo.c trim.c	\
 	  vibro.c vol.c
+libsfx_la_CFLAGS = @SAMPLERATE_CFLAGS@
+libsfx_la_LIBADD = @SAMPLERATE_LIBS@
 
-libsox_la_SOURCES = $(effects) adpcms.c adpcms.h aiff.c aiff.h cvsd.c cvsd.h cvsdfilt.h \
+libsox_la_SOURCES = adpcms.c adpcms.h aiff.c aiff.h cvsd.c cvsd.h cvsdfilt.h \
 	  g711.c g711.h g721.c g723_24.c g723_40.c g72x.c g72x.h vox.c vox.h	\
 	  raw.c raw.h handlers.c misc.c sox_i.h skelform.c soxio.c	\
 	  util.c xmalloc.c xmalloc.h getopt.c getopt1.c getopt.h	\
 	  soxconfig.h effects.c
-libsox_la_CFLAGS = @SAMPLERATE_CFLAGS@
-libsox_la_LIBADD = @LIBGSM_LIBADD@ @SAMPLERATE_LIBS@
 
 sox_SOURCES = sox.c
-sox_LDADD = libsox.la
+sox_LDADD = libsox.la libsfx.la
 
 EXTRA_DIST = tests.sh testall.sh tests.bat testall.bat monkey.au monkey.wav
 
--- a/src/ladspa.c
+++ b/src/ladspa.c
@@ -1,5 +1,5 @@
 /*
- * LADSPA effectsupport for sox
+ * LADSPA effect support for sox
  * (c) Reuben Thomas <rrt@sc3d.org> 2007
  *
  * This library is free software; you can redistribute it and/or
@@ -21,6 +21,9 @@
 
 #ifdef HAVE_LADSPA_H
 
+#include <assert.h>
+#include <limits.h>
+#include <string.h>
 #include <ltdl.h>
 #include <ladspa.h>
 
@@ -28,20 +31,29 @@
 
 /* Private data for resampling */
 typedef struct {
-  char *name;                   /* Plugin name */
-  lt_dlhandle lth;
+  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;
 
 /*
  * Process options
  */
-static int sox_ladspa_getopts(sox_effect_t * effp, int n, char **argv)
+static int sox_ladspa_getopts(sox_effect_t *effp, int n, char **argv)
 {
   ladspa_t l_st = (ladspa_t)effp->priv;
   char *path;
-  LADSPA_Descriptor *l_desc;
   LADSPA_Descriptor_Function l_fn;
+  unsigned long index = 0, i;
+  unsigned long audio_ports = 0;
+  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];
@@ -59,14 +71,94 @@
   }
 
   /* Get descriptor function */
-  if ((l_fn = lt_dlsym(l_st->lth, "ladspa_descriptor") == NULL)) {
+  if ((l_fn = 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 (l_fn(0) == NULL) {
+    sox_fail("no plugins found");
+    return SOX_EOF;
+  }
+
+  /* Get first plugin descriptor */
+  l_st->desc = l_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 (l_fn(1) != NULL || !sscanf(argv[0], "%lf", &arg)) {
+    while (l_st->desc && strcmp(l_st->desc->Label, argv[0]) != 0)
+      l_st->desc = l_fn(++index);
+    if (l_st->desc == NULL) {
+      sox_fail("no plugin called `%s' found", argv[0]);
+      return SOX_EOF;
+    } else
+      n--; argv++;
+  }
+
+
+  /* Instantiate the plugin */
+  l_st->handle = l_st->desc->instantiate(l_st->desc, effp->ininfo.rate);
+  if (l_st->handle == NULL) {
+    sox_fail("could not instantiate plugin");
+    return SOX_EOF;
+  }
+
+  /* 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 %d is both input and output", i);
+      return SOX_EOF;
+    } else if (LADSPA_IS_PORT_CONTROL(port) && LADSPA_IS_PORT_AUDIO(port)) {
+      sox_fail("port %d is both audio and control", i);
+      return SOX_EOF;
+    }
+               
+    if (LADSPA_IS_PORT_AUDIO(port)) {
+      audio_ports++;
+      if (audio_ports > 2) {
+        sox_fail("can't use a plugin with more than two audio ports");
+        return SOX_EOF;
+      }
+
+      /* Don't bother counting input and output ports, as if we have
+         too many or not enough we'll find out anyway */
+      if (LADSPA_IS_PORT_INPUT(port))
+        l_st->input_port = i;
+      else if (LADSPA_IS_PORT_OUTPUT(port))
+        l_st->output_port = i;
+    } else {                    /* Control port */
+      if (n == 0) {
+        sox_fail("not enough arguments for control ports");
+        return SOX_EOF;
+      }
+      if (!sscanf(argv[0], "%lf", &arg)) {
+        sox_fail(effp->handler.usage);
+        return SOX_EOF;
+      }
+      l_st->control[i] = (LADSPA_Data)arg;
+      sox_debug("argument for port %d is %f", i, l_st->control[i]);
+      n--; argv++;
+      l_st->desc->connect_port(l_st->handle, i, &(l_st->control[i]));
+    }
+  }
+
+  /* FIXME: allow use of source and sink plugins */
+  if (l_st->input_port == ULONG_MAX) {
+    sox_fail("no input port");
+    return SOX_EOF;
+  } else if (l_st->output_port == ULONG_MAX) {
+    sox_fail("no output port");
+    return SOX_EOF;
+  }
   
-  /* If more than one plugin, next argument is plugin name */
-//   if (ladspa_descriptor
-  
   /* Stop if we have any unused arguments */
   if (n > 0) {
     sox_fail(sox_ladspa_effect.usage);
@@ -83,6 +175,10 @@
 {
   ladspa_t l_st = (ladspa_t)effp->priv;
 
+  /* If needed, activate the plugin */
+  if (l_st->desc->activate)
+    l_st->desc->activate(l_st->handle);
+
   return SOX_SUCCESS;
 }
 
@@ -89,21 +185,39 @@
 /*
  * Process one bufferful of data.
  */
-static int sox_ladspa_flow(sox_effect_t * effp, const sox_ssample_t *ibuf, sox_ssample_t *obuf UNUSED,
+static int sox_ladspa_flow(sox_effect_t * effp, const sox_ssample_t *ibuf, sox_ssample_t *obuf,
                            sox_size_t *isamp, sox_size_t *osamp)
 {
   ladspa_t l_st = (ladspa_t)effp->priv;
+  LADSPA_Data *buf = xmalloc(sizeof(LADSPA_Data) * *isamp);
+  sox_sample_t i;
 
+  /* Copy the input; FIXME: Assume LADSPA_Data == float! */
+  for (i = 0; i < *isamp; i++)
+    buf[i] = SOX_SAMPLE_TO_FLOAT_32BIT(ibuf[i], effp->clips);
+
+  /* Connect the I/O ports */
+  l_st->desc->connect_port(l_st->handle, l_st->input_port, buf);
+  l_st->desc->connect_port(l_st->handle, l_st->output_port, buf);
+
+  /* Run the plugin */
+  l_st->desc->run(l_st->handle, *isamp);
+
+  /* Copy the output; FIXME: Assume LADSPA_Data == float! */
+  *osamp = *isamp;
+  for (i = 0; i < *osamp; i++)
+    obuf[i] = SOX_FLOAT_32BIT_TO_SAMPLE(buf[i], effp->clips);
+
+  free(buf);
+
   return SOX_SUCCESS;
 }
 
 /*
- * Close down the effect.
+ * Nothing to do.
  */
-static int sox_ladspa_drain(sox_effect_t * effp, sox_ssample_t *obuf, sox_size_t *osamp)
+static int sox_ladspa_drain(sox_effect_t * effp UNUSED, sox_ssample_t *obuf UNUSED, sox_size_t *osamp)
 {
-  ladspa_t l_st = (ladspa_t)effp->priv;
-
   *osamp = 0;
 
   return SOX_SUCCESS;
@@ -116,6 +230,10 @@
 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;
 }
--- a/src/sox.h
+++ b/src/sox.h
@@ -422,7 +422,7 @@
 extern int sox_seek(ft_t ft, sox_size_t offset, int whence);
 
 sox_effect_handler_t const *sox_find_effect(char const * name);
-int sox_create_effect(sox_effect_t * effp, sox_effect_handler_t const *e);
+void sox_create_effect(sox_effect_t * effp, sox_effect_handler_t const *e);
 int sox_update_effect(sox_effect_t * effp, const sox_signalinfo_t *in, const sox_signalinfo_t *out, int effect_mask);
 
 /* Effects chain */
--- a/src/util.c
+++ b/src/util.c
@@ -191,7 +191,7 @@
   return (SOX_SUCCESS);
 }
 
-int sox_create_effect(sox_effect_t * effp, sox_effect_handler_t const * e)
+void sox_create_effect(sox_effect_t * effp, sox_effect_handler_t const * e)
 {
   assert(e);
   memset(effp, 0, sizeof(*effp));
@@ -203,7 +203,6 @@
   if (!effp->handler.drain) effp->handler.drain = effect_nothing_drain;
   if (!effp->handler.stop) effp->handler.stop = effect_nothing;
   if (!effp->handler.kill) effp->handler.kill = effect_nothing;
-  return SOX_SUCCESS;
 }
 
 /*