shithub: sox

Download patch

ref: 8b4f02ed4de617adb9cab6ebac890882248271f7
parent: 1f3073531aeba93810a671e788e25f3e6428c2a7
author: robs <robs>
date: Fri Dec 22 17:52:19 EST 2006

Added silence padding effect.

--- a/AUTHORS
+++ b/AUTHORS
@@ -90,5 +90,5 @@
 	Rob Sykes		robs@users.sourceforge.net
 		Bass & treble effects, FLAC support, initial 24bit
 		support, Octave plotting for filters, new flanger,
-                file merging, various small fixes, enchancements
-                and clean-ups.
+                file merging, speed via resampling, pad effect,
+                various small fixes, enchancements and clean-ups.
--- a/ChangeLog
+++ b/ChangeLog
@@ -77,6 +77,7 @@
     problematic -v option.  Use the vol effect instead.  (robs)
   o Added prompt to overwrite pre-existing output file (unless overridden
     with --force).  (robs)
+  o Added silence padding effect.  (robs)
 
 sox-12.18.2
 -----------
--- a/sox.1
+++ b/sox.1
@@ -36,7 +36,7 @@
 .SH DESCRIPTION
 .I SoX
 reads and writes most popular audio formats and can optionally apply
-effects to them; it includes a basic audio synthesiser, and on unix-like
+effects to them; it includes a basic audio synthesiser, and on Unix-like
 systems, can play and record audio files.
 .P
 .I SoX
@@ -61,7 +61,7 @@
 that follows.
 The second type is `headerless', often called raw data.  For a file
 of this type, the audio data characteristics are sometimes described by
-the filename extension, sometimes by giving format options on the
+the file-name extension, sometimes by giving format options on the
 .I SoX
 command line, and otherwise by a combination of the two.
 .P
@@ -70,7 +70,7 @@
 .TP 10
 sample rate
 The sample rate in samples per second (or Hz).  For example, digital telephony
-tradionally uses a ample rate of 8000Hz; CDs use 44,100Hz.
+traditionally uses a sample rate of 8000Hz; CDs use 44,100Hz.
 .TP 10
 sample size
 The number of bits (or bytes) used to store each sample.  Most popular are
@@ -83,7 +83,7 @@
 linear, FLAC, etc.
 .TP 10
 channels
-The number of audio channels contained in the file.  1 (`mono') and 2
+The number of audio channels contained in the file.  One (`mono') and two
 (`stereo') are widely used.
 .P
 The term `bit-rate' is sometimes used as an overall measure of an audio
@@ -99,7 +99,7 @@
 is wanted, then format options can be used to specify the differences.
 .PP
 If an output file format does not support the same data type, sample
-rate, or channel count as the input file format, then unless overriden
+rate, or channel count as the input file format, then unless overridden
 on the command line, \fISoX\fR will automatically select the closest values
 that the format does support.
 .P
@@ -112,7 +112,7 @@
 otherwise,
 .I SoX
 will try first using the file header (input files only), and then
-the filename extension to determine the file type.
+the file-name extension to determine the file type.
 If the file type cannot be determined, then
 .I SoX
 will exit with an error.
@@ -148,10 +148,10 @@
 other effects, when converting one format to another, and even when
 simply playing the audio.
 
-Playing an audio file often involves resampling, and processing by
+Playing an audio file often involves re-sampling, and processing by
 analogue components that can introduce a small DC offset and/or
 amplification, all of which can produce distortion if the audio signal
-level was intially too close to the clipping point.
+level was initially too close to the clipping point.
 
 For these reasons, it is usual to make sure that an audio
 file's signal level does not exceed around 70% of the maximum (linear)
@@ -233,24 +233,24 @@
 .B soxexam(1)
 manual page for a more detailed description of
 .I SoX
-and futher examples on how to use
+and further examples on how to use
 .I SoX
 with various file formats and effects.
 .PP
 .SH OPTIONS
-\fBSpecial Filename Options\fR
+\fBSpecial File-name Options\fR
 .br
 Each of these options is used in special circumstances in place of a normal
-filename on the command line.
+file-name on the command line.
 .TP 10
 \fB-\fR
 \fISoX\fR can be used in pipeline operations by using the special
-filename `-' which,
-if used in place of input filename, will cause
+file-name `-' which,
+if used in place of input file-name, will cause
 .I SoX
 will read audio data from stdin,
 and which,
-if used in place of output filename, will cause
+if used in place of output file-name, will cause
 .I SoX
 will send audio data to stdout.
 Note that when using this option,
@@ -258,7 +258,7 @@
 must also be given.
 .TP 10
 \fB-n\fR
-This can be used in place of an input or output filename
+This can be used in place of an input or output file-name
 to specify that the `null' file type should be used. See
 .B null
 below for further information.
@@ -333,7 +333,7 @@
 .RS
 .IP 0
 No messages are printed at all; use the exit status to determine
-if an error has ocurred.
+if an error has occurred.
 .IP 1
 Only error messages are printed. These are generated if
 .I SoX
@@ -370,7 +370,7 @@
 \fBInput File Options\fR
 .br
 These options apply to only input files and may only precede input
-filenames on the command line.
+file-names on the command line.
 .TP 10
 \fB-v \fIvolume\fR
 Adjust volume by a factor of \fIvolume\fR.
@@ -416,7 +416,7 @@
 multiple rate changing effects, the user can specify which to use as an effect.
 If no rate change effect is specified then a default one will be chosen.
 .TP 10
-\fB-t \fIfiletype\fR
+\fB-t \fIfile-type\fR
 Gives the type of the audio file.  This is useful when the
 file extension is non-standard or when the type can not be determined by
 looking at the header of the file.
@@ -423,7 +423,7 @@
 
 The
 .B -t
-option can also be used to override the type implied by an input filename
+option can also be used to override the type implied by an input file-name
 extension, but if overriding with a type that has a header,
 .I SoX
 will exit with an appropriate error message if such a header is not
@@ -484,13 +484,13 @@
 \fBOutput File Format Options\fR
 .br
 These options apply to only the output file and may only precede the output
-filename on the command line.
+file-name on the command line.
 .TP 10
 \fB--comment \fItext\fR
 Specify the comment text to store in the output file header (where
 applicable).
 .TP 10
-\fB--comment-file \fIfilename\fR
+\fB--comment-file \fIfile-name\fR
 Specify a file containing the comment text to store in the output
 file header (where applicable).
 .TP 10
@@ -502,7 +502,7 @@
 use this option for more information.
 .SH FILE TYPES
 Note: a file type that can be determined
-by filename extension is listed with its name preceded by a dot.
+by file-name extension is listed with its name preceded by a dot.
 .PP
 .TP 10
 .B .8svx
@@ -644,7 +644,7 @@
 GSM in
 .I SoX
 is optional and requires access to an external GSM library.  To see
-if there is support for gsm run \fBsox -h\fR
+if there is support for GSM run \fBsox -h\fR
 and look for it under the list of supported file formats.
 .TP 10
 .B .hcom
@@ -657,7 +657,7 @@
 to deal with an HCOM file under Unix or DOS.
 .TP 10
 .B .maud
-An IFF-conformant audio file type, registered by
+An IFF-conforming audio file type, registered by
 MS MacroSystem Computer GmbH, published along
 with the `Toccata' sound-card on the Amiga.
 Allows 8bit linear, 16bit linear, A-Law, u-law
@@ -684,9 +684,9 @@
 This is a special file type that can be used when normal
 file reading or writing is not needed to use a particular effect.
 It is selected by using the
-special filename
+special file-name
 .B -n
-in place of an input or output filename.
+in place of an input or output file-name.
 
 Using this file type to input audio is equivalent to
 using a normal audio file that contains an infinite amount
@@ -700,7 +700,7 @@
 (such as \fBnoiseprof\fR or \fBstat\fR).
 
 The number of channels and the sampling rate associated with a null file
-are by default 2 and 44.1kHz respectively, but these can be overriden
+are by default 2 and 44.1kHz respectively, but these can be overridden
 if necessary by using appropriate \fBFormat Options\fR.
 
 One other use of the null file type is to use it in conjunction
@@ -1273,6 +1273,35 @@
 Experiment with different threshold values to find the optimal one for your
 sample.
 .TP 10
+pad {\fIlength\fR[\fI@position\fR]}
+Pad the audio with silence, at the beginning, the end, or any
+specified points through the audio.
+Both
+.I length
+and
+.I position
+can specify a time or, if appended with an `s', a number of samples.
+.I length
+is the amount of silence to insert and
+.I position
+the position in the input audio stream at which to insert it.
+Any number of lengths and positions may be specified, provided that
+a specified position is not less that the previous one.
+.I position
+is optional for the first and last lengths specified and
+if omitted correspond to the beginning and the end of the audio respectively.
+For example:
+
+	pad 1.5 1.5
+
+adds 1.5 seconds of silence padding at each end of the audio, whilst
+
+	pad 4000s@3:00
+
+inserts 4000 samples of silence 3 minutes into the audio.
+If silence is wanted only at the end of the audio, specify either the end
+position or specify a zero-length pad at the start.
+.TP 10
 pan \fIdirection\fB
 Pan the audio from one channel to another.  This is done by
 changing the volume of the input channels so that it fades out on one
@@ -1301,7 +1330,7 @@
 effect
 but is left here for historical reasons.
 .TP 10
-pitch \fIshift [ width interpole fade ]\fB
+pitch \fIshift [ width interpolate fade ]\fB
 Change the pitch of file without affecting its duration by cross-fading
 shifted samples.
 .I shift
@@ -1310,7 +1339,7 @@
 .I width
 of window is in ms. Default width is 20ms. Try 30ms to lower pitch,
 and 10ms to raise pitch.
-.I interpole
+.I interpolate
 option, can be `cubic' or `linear'. Default is `cubic'.  The
 .I fade
 option, can be `cos', `hamming', `linear' or `trapezoid'.
@@ -1325,8 +1354,8 @@
 interpolation, a DSP algorithm.  This method is relatively slow and memory intensive.
 
 .br
--w < nut / ham > : select either a Nuttall (~90 dB stopband) or Hamming
-(~43 dB stopband) window.  Default is
+-w < nut / ham > : select either a Nuttall (~90 dB stop-band) or Hamming
+(~43 dB stop-band) window.  Default is
 .I nut.
 
 .br
@@ -1382,16 +1411,16 @@
 
 By default, linear interpolation is used,
 with a window width about 45 samples at the lower of the two rates.
-This gives an accuracy of about 16 bits, but insufficient stopband rejection
-in the case that you want to have rolloff greater than about 0.80 of
+This gives an accuracy of about 16 bits, but insufficient stop-band rejection
+in the case that you want to have roll-off greater than about 0.80 of
 the Nyquist frequency.
 
-The \fI-q*\fR options will change the default values for rolloff and beta
+The \fI-q*\fR options will change the default values for roll-off and beta
 as well as use quadratic interpolation of filter
 coefficients, resulting in about 24 bits precision.
 The \fI-qs\fR, \fI-q\fR, or \fI-ql\fR options specify increased accuracy
 at the cost of lower execution speed.  It is optional to specify
-rolloff and beta parameters when using the \fI-q*\fR options.
+roll-off and beta parameters when using the \fI-q*\fR options.
 
 Following is a table of the reasonable defaults which are built-in to
 \fISoX\fR:
@@ -1419,7 +1448,7 @@
 \fIrolloff\fR refers to the cut-off frequency of the
 low pass filter and is given in terms of the
 Nyquist frequency for the lower sample rate.  rolloff therefore should
-be something between 0.0 and 1.0, in practice 0.8-0.95.  The defaults are
+be something between 0.0 and 1.0, in practise 0.8-0.95.  The defaults are
 indicated above.
 
 The \fINyquist frequency\fR is equal to (sample rate / 2).  Logically,
@@ -1438,10 +1467,10 @@
 is, with closer being better.  When increasing the sample rate of an
 audio file you would not expect to have any frequencies exist that are
 past the original Nyquist frequency.  Because of resampling properties, it
-is common to have aliasing artefacts created above the old
+is common to have aliasing artifacts created above the old
 Nyquist frequency.  In that case the \fIrolloff\fR refers to how close
 to the original Nyquist frequency to use a highpass filter to remove
-these artefacts, with closer also being better.
+these artifacts, with closer also being better.
 
 The \fIbeta\fR parameter
 determines the type of filter window used.  Any value greater than 2.0 is
@@ -1449,7 +1478,7 @@
 If unspecified, the default is a Kaiser window with beta 16.
 
 In the case of Kaiser window (beta > 2.0), lower betas produce a somewhat
-faster transition from passband to stopband, at the cost of noticeable artifacts.
+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
@@ -1459,7 +1488,7 @@
 
 This is the default effect if the two files have different sampling rates.
 Default parameters are, as indicated above, Kaiser window of length 45,
-rolloff 0.80, beta 16, linear interpolation.
+roll-off 0.80, beta 16, linear interpolation.
 
 \fBNOTE:\fR \fI-qs\fR is only slightly slower, but more accurate for
 16-bit or higher precision.
@@ -1560,7 +1589,7 @@
 stat [ \fI-s N\fB ] [\fI-rms\fB ] [\fI-freq\fB ] [ \fI-v\fB ] [ \fI-d\fB ]
 Do a statistical check on the input file,
 and print results on the standard error file.  Audio is passed
-unmodified throught the
+unmodified through the
 .I SoX
 processing chain.
 
@@ -1631,7 +1660,7 @@
 .TP 10
 synth [\fIlen\fR] {[\fItype] [combine\fR] [\fIfreq\fR[\fI-freq2\fR]] [\fIoff\fR] [\fIph\fR] [\fIp1\fR] [\fIp2\fR] [\fIp3\fR]}
 This effect can be used to generate fixed or swept frequency audio tones
-with various wave shapes, or to generate wideband noise of various
+with various wave shapes, or to generate wide-band noise of various
 `colours'.
 Multiple synth effects can be cascaded to produce more complex
 waveforms; at each stage it is possible to choose whether the generated
@@ -1669,7 +1698,7 @@
 
 	sox -n output.au synth .5 sine 200-500 synth .5 sine fmod 700-100
 
-Frequencies can also specied in terms of musical semitones relative to
+Frequencies can also specified in terms of musical semitones relative to
 `middle A' (440Hz);  the following could be used to help tune
 a guitar's `low E' string (on a system that supports
 \fBalsa\fR):
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -18,7 +18,7 @@
 	  btrworth.h chorus.c compand.c copy.c dcshift.c deemphas.c earwax.c \
 	  echo.c echos.c equalizer.c fade.c FFT.c FFT.h filter.c flanger.c \
 	  highp.c highpass.c lowp.c lowpass.c lua.c lintlib.c mask.c \
-	  mcompand.c noiseprof.c noisered.c noisered.h pan.c \
+	  mcompand.c noiseprof.c noisered.c noisered.h pad.c pan.c \
 	  phaser.c pitch.c polyphas.c rabbit.c rate.c repeat.c resample.c \
 	  reverb.c reverse.c silence.c speed.c stat.c \
 	  stretch.c swap.c synth.c tone.c trim.c vibro.c vol.c
--- a/src/fade.c
+++ b/src/fade.c
@@ -81,8 +81,7 @@
     fade->in_stop_str = (char *)xmalloc(strlen(argv[0])+1);
     strcpy(fade->in_stop_str,argv[0]);
     /* Do a dummy parse to see if it will fail */
-    if (st_parsesamples(0, fade->in_stop_str, &fade->in_stop, 't') !=
-            ST_SUCCESS)
+    if (st_parsesamples(0, fade->in_stop_str, &fade->in_stop, 't') == NULL)
     {
         st_fail(st_fade_effect.usage);
         return(ST_EOF);
@@ -100,7 +99,7 @@
 
             /* Do a dummy parse to see if it will fail */
             if (st_parsesamples(0, fade->out_stop_str, 
-                                &fade->out_stop, 't') != ST_SUCCESS) {
+                                &fade->out_stop, 't') == NULL) {
               st_fail(st_fade_effect.usage);
               return(ST_EOF);
             }
@@ -112,7 +111,7 @@
 
             /* Do a dummy parse to see if it will fail */
             if (st_parsesamples(0, fade->out_start_str, 
-                                &fade->out_start, 't') != ST_SUCCESS) {
+                                &fade->out_start, 't') == NULL) {
               st_fail(st_fade_effect.usage);
               return(ST_EOF);
             }
@@ -133,7 +132,7 @@
     /* converting time values to samples */
     fade->in_start = 0;
     if (st_parsesamples(effp->ininfo.rate, fade->in_stop_str,
-                        &fade->in_stop, 't') != ST_SUCCESS)
+                        &fade->in_stop, 't') == NULL)
     {
         st_fail(st_fade_effect.usage);
         return(ST_EOF);
@@ -145,7 +144,7 @@
     {
         fade->do_out = 1;
         if (st_parsesamples(effp->ininfo.rate, fade->out_stop_str,
-                            &fade->out_stop, 't') != ST_SUCCESS)
+                            &fade->out_stop, 't') == NULL)
         {
             st_fail(st_fade_effect.usage);
             return(ST_EOF);
@@ -155,7 +154,7 @@
         if (fade->out_start_str)
         {
             if (st_parsesamples(effp->ininfo.rate, fade->out_start_str,
-                        &fade->out_start, 't') != ST_SUCCESS)
+                        &fade->out_start, 't') == NULL)
             {
                 st_fail(st_fade_effect.usage);
                 return(ST_EOF);
--- a/src/handlers.c
+++ b/src/handlers.c
@@ -112,6 +112,7 @@
   st_mcompand_effect_fn,
   st_noiseprof_effect_fn,
   st_noisered_effect_fn,
+  st_pad_effect_fn,
   st_pan_effect_fn,
   st_phaser_effect_fn,
   st_pick_effect_fn,
--- /dev/null
+++ b/src/pad.c
@@ -1,0 +1,135 @@
+/*  Sound Tools Effect: Pad With Silence
+ *
+ *  (c) 2006 robs@users.sourceforge.net
+ *
+ *  See LICENSE file for further copyright information.
+ */
+
+#include "st_i.h"
+#include <string.h>
+
+typedef struct pad
+{
+  int npads;         /* Number of pads requested */
+  struct {
+    char * str;      /* Comman-line argument to parse for this pad */
+    st_size_t start; /* Start padding when in_pos equals this */
+    st_size_t pad;   /* Number of samples to pad */
+  } * pads;
+
+  st_size_t in_pos;  /* Number of samples read from the input stream */
+  int pads_pos;      /* Number of pads completed so far */
+  st_size_t pad_pos; /* Number of samples through the current pad */
+} * pad_t;
+
+assert_static(sizeof(struct pad) <= ST_MAX_EFFECT_PRIVSIZE,
+              /* else */ pad_PRIVSIZE_too_big);
+
+static int parse(eff_t effp, char **argv, st_rate_t rate)
+{
+  pad_t p = (pad_t) effp->priv;
+  char const * next;
+  int i;
+
+  for (i = 0; i < p->npads; ++i) {
+    if (argv) /* 1st parse only */
+      p->pads[i].str = xstrdup(argv[i]);
+    next = st_parsesamples(rate, p->pads[i].str, &p->pads[i].pad, 't');
+    if (next == NULL) break;
+    if (*next == '\0')
+      p->pads[i].start = i? ST_SIZE_MAX : 0;
+    else {
+      if (*next != '@') break;
+      next = st_parsesamples(rate, next+1, &p->pads[i].start, 't');
+      if (next == NULL || *next != '\0') break;
+    }
+    if (i > 0 && p->pads[i].start <= p->pads[i-1].start) break;
+  }
+  if (i < p->npads) {
+    st_fail(effp->h->usage);
+    return ST_EOF;
+  }
+  return ST_SUCCESS;
+}
+
+static int st_pad_getopts(eff_t effp, int n, char **argv)
+{
+  pad_t p = (pad_t) effp->priv;
+  p->pads = xcalloc(p->npads = n, sizeof(*p->pads));
+  return parse(effp, argv, ST_MAXRATE); /* No rate yet, parse with dummy */
+}
+
+static int st_pad_start(eff_t effp)
+{
+  pad_t p = (pad_t) effp->priv;
+  int i;
+
+  parse(effp, 0, effp->ininfo.rate); /* Re-parse now rate is known */
+  p->in_pos = p->pad_pos = p->pads_pos = 0;
+  for (i = 0; i < p->npads; ++i)
+    if (p->pads[i].pad)
+      return ST_SUCCESS;
+  return ST_EFF_NULL;
+}
+
+static int st_pad_flow(eff_t effp, const st_sample_t * ibuf, st_sample_t * obuf, st_size_t * isamp, st_size_t * osamp)
+{
+  pad_t p = (pad_t) effp->priv;
+  st_size_t c, idone = 0, odone = 0;
+  *isamp /= effp->ininfo.channels;
+  *osamp /= effp->ininfo.channels;
+
+  do {
+    /* Copying: */
+    for (; idone < *isamp && odone < *osamp && !(p->pads_pos != p->npads && p->in_pos == p->pads[p->pads_pos].start); ++idone, ++odone, ++p->in_pos)
+      for (c = 0; c < effp->ininfo.channels; ++c) *obuf++ = *ibuf++;
+
+    /* Padding: */
+    if (p->pads_pos != p->npads && p->in_pos == p->pads[p->pads_pos].start) {
+      for (; odone < *osamp && p->pad_pos < p->pads[p->pads_pos].pad; ++odone, ++p->pad_pos)
+        for (c = 0; c < effp->ininfo.channels; ++c) *obuf++ = 0;
+      if (p->pad_pos == p->pads[p->pads_pos].pad) { /* Move to next pad? */
+        ++p->pads_pos;
+        p->pad_pos = 0;
+      }
+    }
+  } while (idone < *isamp && odone < *osamp);
+
+  *isamp = idone * effp->ininfo.channels;
+  *osamp = odone * effp->ininfo.channels;
+  return ST_SUCCESS;
+}
+
+static int st_pad_drain(eff_t effp, st_sample_t * obuf, st_size_t * osamp)
+{
+  static st_size_t isamp = 0;
+  pad_t p = (pad_t) effp->priv;
+  if (p->pads_pos != p->npads && p->in_pos != p->pads[p->pads_pos].start)
+    p->in_pos = ST_SIZE_MAX;  /* Invoke the final pad (with no given start) */
+  return st_pad_flow(effp, 0, obuf, &isamp, osamp);
+}
+
+static int st_pad_stop(eff_t effp)
+{
+  pad_t p = (pad_t) effp->priv;
+  if (p->pads_pos != p->npads)
+    st_warn("Input audio too short; pads not applied: %i",p->npads-p->pads_pos);
+  /* Don't free stuff from getopts; start & stop must be symmetric */
+  return ST_SUCCESS;
+}
+
+static st_effect_t st_pad_effect = {
+  "pad",
+  "Usage: pad {length[@position]}",
+  ST_EFF_MCHAN,
+  st_pad_getopts,
+  st_pad_start,
+  st_pad_flow,
+  st_pad_drain,
+  st_pad_stop
+};
+
+const st_effect_t *st_pad_effect_fn(void)
+{
+  return &st_pad_effect;
+}
--- a/src/play.in
+++ b/src/play.in
@@ -82,9 +82,9 @@
 EFFECTs are one or more of the following: avg, band, bandpass, bandreject,
 bass, chorus, compand, copy, dcshift, deemph, dither, earwax, echo, echos,
 equalizer, fade, filter, flanger, highpass, highp, lowpass, lowp, mask,
-mcompand, noiseprof, noisered, pan, phaser, pick, pitch, polyphase, rabbit,
-rate, repeat, resample, reverb, reverse, silence, speed, stat, stretch,
-swap, synth, treble, trim, vibro, vol.
+mcompand, noiseprof, noisered, pad, pan, phaser, pick, pitch, polyphase,
+rabbit, rate, repeat, resample, reverb, reverse, silence, speed, stat,
+stretch, swap, synth, treble, trim, vibro, vol.
 
 See sox man page for detailed information on supported file types, data
 formats, and effect options."
@@ -94,7 +94,7 @@
 # loop over arguments
 while [ $# -ne 0 ]; do
     case "$1" in
-	avg|band|bandpass|bandreject|bass|chorus|compand|copy|dcshift|deemph|dither|earwax|echo|echos|equalizer|fade|filter|flanger|highpass|highp|lowpass|lowp|mask|mcompand|noiseprof|noisered|pan|phaser|pick|pitch|polyphase|rabbit|rate|repeat|resample|reverb|reverse|silence|speed|stat|stretch|swap|synth|treble|trim|vibro|vol)
+	avg|band|bandpass|bandreject|bass|chorus|compand|copy|dcshift|deemph|dither|earwax|echo|echos|equalizer|fade|filter|flanger|highpass|highp|lowpass|lowp|mask|mcompand|noiseprof|noisered|pad|pan|phaser|pick|pitch|polyphase|rabbit|rate|repeat|resample|reverb|reverse|silence|speed|stat|stretch|swap|synth|treble|trim|vibro|vol)
 	    effects="$@"
 	    break
 	    ;;
--- a/src/silence.c
+++ b/src/silence.c
@@ -121,8 +121,7 @@
         strcpy(silence->start_duration_str,argv[0]);
         /* Perform a fake parse to do error checking */
         if (st_parsesamples(0,silence->start_duration_str,
-                    &silence->start_duration,'s') !=
-                ST_SUCCESS)
+                    &silence->start_duration,'s') == NULL)
         {
             st_fail(st_silence_effect.usage);
             return(ST_EOF);
@@ -175,8 +174,7 @@
         strcpy(silence->stop_duration_str,argv[0]);
         /* Perform a fake parse to do error checking */
         if (st_parsesamples(0,silence->stop_duration_str,
-                    &silence->stop_duration,'s') !=
-                ST_SUCCESS)
+                    &silence->stop_duration,'s') == NULL)
         {
             st_fail(st_silence_effect.usage);
             return(ST_EOF);
@@ -259,8 +257,7 @@
         if (silence->start)
         {
             if (st_parsesamples(effp->ininfo.rate, silence->start_duration_str,
-                                &silence->start_duration, 's') !=
-                    ST_SUCCESS)
+                                &silence->start_duration, 's') == NULL)
             {
                 st_fail(st_silence_effect.usage);
                 return(ST_EOF);
@@ -269,8 +266,7 @@
         if (silence->stop)
         {
             if (st_parsesamples(effp->ininfo.rate,silence->stop_duration_str,
-                                &silence->stop_duration,'s') !=
-                    ST_SUCCESS)
+                                &silence->stop_duration,'s') == NULL)
             {
                 st_fail(st_silence_effect.usage);
                 return(ST_EOF);
--- a/src/st.h
+++ b/src/st.h
@@ -401,7 +401,7 @@
 int st_updateeffect(eff_t, const st_signalinfo_t *in, const st_signalinfo_t *out, int);
 int st_gettype(ft_t, bool);
 ft_t st_initformat(void);
-int st_parsesamples(st_rate_t rate, const char *str, st_size_t *samples, char def);
+char const * st_parsesamples(st_rate_t rate, const char *str, st_size_t *samples, char def);
 
 extern char const * st_message_filename;
 
--- a/src/st_i.h
+++ b/src/st_i.h
@@ -291,6 +291,7 @@
 extern const st_effect_t *st_mcompand_effect_fn(void);
 extern const st_effect_t *st_noiseprof_effect_fn(void);
 extern const st_effect_t *st_noisered_effect_fn(void);
+extern const st_effect_t *st_pad_effect_fn(void);
 extern const st_effect_t *st_pan_effect_fn(void);
 extern const st_effect_t *st_phaser_effect_fn(void);
 extern const st_effect_t *st_pick_effect_fn(void);
--- a/src/synth.c
+++ b/src/synth.c
@@ -279,8 +279,7 @@
         synth->length_str = (char *)xmalloc(strlen(argv[argn])+1);
         strcpy(synth->length_str,argv[argn]);
         /* Do a dummy parse of to see if it will fail */
-        if (st_parsesamples(0, synth->length_str, &synth->length, 't') !=
-                ST_SUCCESS)
+        if (st_parsesamples(0, synth->length_str, &synth->length, 't') == NULL)
         {
             st_fail(st_synth_effect.usage);
             return (ST_EOF);
@@ -379,7 +378,7 @@
     if (synth->length_str)
     {
         if (st_parsesamples(effp->ininfo.rate, synth->length_str,
-                            &synth->length, 't') != ST_SUCCESS)
+                            &synth->length, 't') == NULL)
         {
             st_fail(st_synth_effect.usage);
             return(ST_EOF);
--- a/src/trim.c
+++ b/src/trim.c
@@ -49,8 +49,7 @@
             trim->length_str = (char *)xmalloc(strlen(argv[1])+1);
             strcpy(trim->length_str,argv[1]);
             /* Do a dummy parse to see if it will fail */
-            if (st_parsesamples(0, trim->length_str,
-                                &trim->length, 't') != ST_SUCCESS)
+            if (st_parsesamples(0, trim->length_str, &trim->length, 't') == NULL)
             {
                 st_fail(st_trim_effect.usage);
                 return(ST_EOF);
@@ -59,8 +58,7 @@
             trim->start_str = (char *)xmalloc(strlen(argv[0])+1);
             strcpy(trim->start_str,argv[0]);
             /* Do a dummy parse to see if it will fail */
-            if (st_parsesamples(0, trim->start_str,
-                                &trim->start, 't') != ST_SUCCESS)
+            if (st_parsesamples(0, trim->start_str, &trim->start, 't') == NULL)
             {
                 st_fail(st_trim_effect.usage);
                 return(ST_EOF);
@@ -83,7 +81,7 @@
     trim_t trim = (trim_t) effp->priv;
 
     if (st_parsesamples(effp->ininfo.rate, trim->start_str,
-                        &trim->start, 't') != ST_SUCCESS)
+                        &trim->start, 't') == NULL)
     {
         st_fail(st_trim_effect.usage);
         return(ST_EOF);
@@ -94,7 +92,7 @@
     if (trim->length_str)
     {
         if (st_parsesamples(effp->ininfo.rate, trim->length_str,
-                    &trim->length, 't') != ST_SUCCESS)
+                    &trim->length, 't') == NULL)
         {
             st_fail(st_trim_effect.usage);
             return(ST_EOF);
--- a/src/util.c
+++ b/src/util.c
@@ -305,18 +305,31 @@
  * treated as an amount of time.  This is converted into seconds and
  * fraction of seconds and then use the sample rate to calculate
  * # of samples.
- * Returns ST_EOF on error.
+ * Returns NULL on error, pointer to next char to parse otherwise.
  */
-int st_parsesamples(st_rate_t rate, const char *str, st_size_t *samples, char def)
+char const * st_parsesamples(st_rate_t rate, const char *str, st_size_t *samples, char def)
 {
     int found_samples = 0, found_time = 0;
     int time = 0;
     long long_samples;
     float frac = 0;
+    char const * end;
+    char const * pos;
+    bool found_colon, found_dot;
 
-    if (strchr(str, ':') || strchr(str, '.') || str[strlen(str)-1] == 't')
+    for (end = str; *end && strchr("0123456789:.ts", *end); ++end);
+    if (end == str)
+      return NULL;
+
+    pos = strchr(str, ':');
+    found_colon = pos && pos < end;
+    
+    pos = strchr(str, '.');
+    found_dot = pos && pos < end;
+
+    if (found_colon || found_dot || *(end-1) == 't')
         found_time = 1;
-    else if (str[strlen(str)-1] == 's')
+    else if (*(end-1) == 's')
         found_samples = 1;
 
     if (found_time || (def == 't' && !found_samples))
@@ -326,7 +339,7 @@
         while(1)
         {
             if (str[0] != '.' && sscanf(str, "%d", &time) != 1)
-                return ST_EOF;
+                return NULL;
             *samples += time;
 
             while (*str != ':' && *str != '.' && *str != 0)
@@ -343,19 +356,19 @@
         if (*str == '.')
         {
             if (sscanf(str, "%f", &frac) != 1)
-                return ST_EOF;
+                return NULL;
         }
 
         *samples *= rate;
         *samples += (rate * frac) + 0.5;
-        return ST_SUCCESS;
+        return end;
     }
     if (found_samples || (def == 's' && !found_time))
     {
         if (sscanf(str, "%ld", &long_samples) != 1)
-            return ST_EOF;
+            return NULL;
         *samples = long_samples;
-        return ST_SUCCESS;
+        return end;
     }
-    return ST_EOF;
+    return NULL;
 }
--- a/src/xmalloc.c
+++ b/src/xmalloc.c
@@ -56,3 +56,16 @@
 
   return ptr;
 }
+
+/*
+ * Perform a strdup; abort if not possible.
+ */
+char * xstrdup(char const * s)
+{
+  char * ptr = strdup(s);
+
+  if (ptr == NULL)
+    st_fail("out of memory");
+
+  return ptr;
+}
--- a/src/xmalloc.h
+++ b/src/xmalloc.h
@@ -27,5 +27,6 @@
 
 void *xcalloc(size_t nmemb, size_t size);
 void *xrealloc(void *ptr, size_t newsize);
+char * xstrdup(char const * s);
 
 #endif