shithub: sox

Download patch

ref: 8f84c484bf7868abccedafd1963fbb249defc5dc
parent: 7e6aa6d2a98867e607d584dc9847b43375b0b8dd
author: robs <robs>
date: Sun May 31 10:56:49 EDT 2009

synth pluck improvements

--- a/scripts/alert.sh
+++ b/scripts/alert.sh
@@ -26,7 +26,6 @@
 # ---------------------------------------------------------------------------
 
 SOX=../src/sox
-PLAY=../src/play
 
 rm -f 2tones.ul    # Make sure we append to a file that's initially empty
 
@@ -45,4 +44,4 @@
 $SOX -c 1 -r 8000 alert.ul alert.au    # Add a file header
 rm 2tones.ul alert.ul                  # Tidy up intermediate files
 
-$PLAY alert.au
+$SOX alert.au -d
--- a/scripts/synth.sh
+++ b/scripts/synth.sh
@@ -1,6 +1,6 @@
 #!/bin/sh
 
-# SoX script: synth.sh               (c) 2008 robs@users.sourceforge.net
+# SoX script: synth.sh               (c) 2008-9 robs@users.sourceforge.net
 #
 # 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
@@ -17,7 +17,6 @@
 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 
 sox=../src/sox
-play=../src/play
 
 G0="pl %-26 pl %-19 pl %-14 pl %-10 pl %-7 pl %-2"
 A0="pl %-24 pl %-17 pl %-12 pl %-8 pl %-5 pl %0"
@@ -26,12 +25,22 @@
 E0="pl %-22 pl %-17 pl %-10 pl %-5 pl %-1 pl %2"
 Bb0="pl %-23 pl %-16 pl %-11 pl %-7 pl %-4 pl %1"
 
-o="overdrive 40 gain -8"
-e="delay 0 .02 .04 .06 .08 .1 remix - $o fade 0"
-s="$sox -q -n -p synth 0 0 0 80 87"
-l="$sox -q -n -p synth 0 0 0 80 0 0"
+o="overdrive 27 gain -11"
+e="delay 0 .02 .04 .06 .08 .1 remix - overdrive 33 gain -8 fade 0"
+s="$sox -q -n -p synth 0 0 1 60 90"
+l="$sox -q -n -p synth 0 0 0 50 20"
 
-$play -m -v .8 \
+b="$sox -np synth 0 0 0 30 20 pl"
+c3="fade h 0 .75"
+c="fade h 0 .25"
+cs="fade h 0 .25 .05"
+m="fade h 0 .5"
+sb="fade h 0 1"
+r="$sox -np trim 0 .25"
+r2="$sox -np trim 0 .5"
+r3="$sox -np trim 0 .75"
+
+$sox -m -v .8 \
 "|$sox \
 \"|$sox -n -p synth noise fade 0 .5 .48 trim 0 .15\" \
 \"|$sox -n -p synth noise fade h 0 .26 .11 gain -35 lowpass -1 12k\" \
@@ -63,15 +72,15 @@
 \"|$l pl %10 $o trim 0 .5 bend .2,-300,.1\" \
 \"|$l pl %5 $o trim 0 .5 bend .2,-200,.1\" \
 \"|$l pl %0 $o fade 0 .55 .1 bend .2,-200,.1\" \
-\"|$l pl %0 $o fade 0 2 .1\" \
+\"|$l pl %0 $o fade h 0 2 .5\" \
 \"|$sox -n -p trim 0 3.95\" \
 \"|$l pl %12 $o trim 0 .5 bend .2,-200,.1\" \
 \"|$l pl %12 $o trim 0 .5 bend .2,-200,.1\" \
 \"|$l pl %12 $o fade 0 .8 .1 bend .2,-200,.1\" \
 \"|$l pl %12 $o trim 0 .3 bend .1,-200,.1\" \
-\"|$l pl %12 $o trim 0 1.95 bend .3,-50,1\" \
-\"|$l pl %10 $o trim 0 2 bend .3,-50,1\" \
-\"|$l pl %9 $o trim 0 2 gain -3\" \
+\"|$l pl %12 $o fade 0 1.95 .6 bend .0,-50,1.75 gain 3\" \
+\"|$l pl %10 $o fade 0 2 .6 bend .0,-50,1.9\" \
+\"|$l pl %9 $o trim 0 2 gain -1\" \
 \"|$l pl %8 $o fade h 0 1 .3\" \
 \"|$l pl %8 $o fade h 0 1 .1 gain 1.5\" \
 \"|$l pl %2 pl %7 delay 0 .02 remix - $o trim 0 .25\" \
@@ -83,4 +92,100 @@
 \"|$l pl %2 pl %7 delay 0 .02 remix - $o trim 0 .25\" \
 \"|$l pl %-5 $o trim 0 .25\" \
 \"|$l pl %2 pl %7 delay 0 .02 remix - $o fade h 0 6 6\" \
--p gain -4 remix 1 1 flanger"
+-p gain -4 remix 1 1 flanger" \
+"|$sox \
+\"|$sox -n -p trim 0 1.5\" \
+\"|$b G1 $m contrast\" \
+\"|$b A1 $c3 contrast\" \
+\"|$b G#1 $c\" \
+\"|$b A1 $c3\" \
+\"|$r\" \
+\
+\"|$b C2 $cs\" \
+\"|$b C2 $cs\" \
+\"|$r\" \
+\"|$b B1 $cs\" \
+\"|$b C2 $c3\" \
+\"|$r\" \
+\
+\"|$b D2 $cs\" \
+\"|$b D2 $cs\" \
+\"|$r\" \
+\"|$b C#2 $cs\" \
+\"|$b D2 $c\" \
+\"|$b C#2 $m\" \
+\"|$r\" \
+\
+\"|$b D2 $cs\" \
+\"|$b D2 $cs\" \
+\"|$r\" \
+\"|$b C#2 $cs\" \
+\"|$b D2 $c\" \
+\"|$b C#2 $c\" \
+\"|$b D2 $c\" \
+\"|$b E2 $c\" \
+\
+\"|$b A1 $c3 contrast\" \
+\"|$b B1 $c\" \
+\"|$b A1 $c\" \
+\"|$b G#1 $c\" \
+\"|$b A1 $c\" \
+\"|$b B1 $c\" \
+\
+\"|$b C2 $c3\" \
+\"|$b B1 $c\" \
+\"|$b C2 $c\" \
+\"|$b D2 $c\" \
+\"|$b C2 $c\" \
+\"|$b B1 $c\" \
+\
+\"|$b D2 $c3\" \
+\"|$b E2 $c\" \
+\"|$b D2 $c\" \
+\"|$b C#2 $c\" \
+\"|$b D2 $c\" \
+\"|$b E2 $c\" \
+\
+\"|$b D2 $c3\" \
+\"|$b C#2 $c\" \
+\"|$b D2 $c\" \
+\"|$b E2 $c\" \
+\"|$b D2 $c\" \
+\"|$b C#2 $c\" \
+\
+\"|$b A1 $cs\" \
+\"|$b A1 $cs\" \
+\"|$r\" \
+\"|$b G#1 $c\" \
+\"|$b A1 $c3\" \
+\"|$b B1 $c\" \
+\
+\"|$b C2 $c\" \
+\"|$b B1 $c\" \
+\"|$b C2 $c\" \
+\"|$b D2 $c\" \
+\"|$b C2 $c3\" \
+\"|$r\" \
+\
+\"|$b D2 $m\" \
+\"|$b E2 $m\" \
+\"|$b D2 $c\" \
+\"|$b C#2 $c\" \
+\"|$b D2 $c\" \
+\"|$b E2 $c\" \
+\
+\"|$b F2 $m\" \
+\"|$b Bb1 $m\" \
+\"|$b C2 $c\" \
+\"|$b F2 $c\" \
+\"|$b D2 $c\" \
+\"|$b Bb1 $c\" \
+\
+\"|$b E1 $m\" \
+\"|$b E2 $m\" \
+\"|$b D2 $c\" \
+\"|$b E2 $m\" \
+\"|$b B1 $c\" \
+\
+\"|$b E1 $sb\" \
+-p lowpass -1 1k remix 1p-12 1p-12" -d
--- a/scripts/testtones.sh
+++ b/scripts/testtones.sh
@@ -273,7 +273,7 @@
   echo; echo "Plucked scale:"
 
   options=
-  overdrive=
+  overdrive="gain -3"
   for f in pluck pluck_dist; do
     next_file $f 42
     note=-29
@@ -289,14 +289,13 @@
       $input -t s32 - synth .4 pluck %$note $options >> tmp.s32
       note=$(($note - 1))
     done
-    $input -t s32 - synth pluck %$note $options fade h 0 2 .1 >> tmp.s32
+    $input -t s32 - synth pluck %$note $options fade t 0 4 3.6 >> tmp.s32
 
-    $sox -r $rate -c 1 tmp.s32 $output compand .1,.2 6:-70,-60,-20 -7 -15 .1 \
-        $overdrive pad 0 2 remix 1 1 reverb 30
+    $sox -r $rate -c 1 tmp.s32 $output $overdrive remix 1 1 reverb 30
 
     rm -f tmp.s32
-    options="0 0 60 60"
-    overdrive="overdrive 30 gain -9"
+    options="0 0 60 75 0"
+    overdrive="overdrive gain -10"
   done
 fi
 
--- a/sox.1
+++ b/sox.1
@@ -1891,8 +1891,8 @@
 and this plays a guitar chord:
 .EX
 .ne 2
-   play -n synth -j -2  pl %-26 pl %-22 pl %-19 pl %-14 pl %-7 \\
-	pl %-2 delay 0 .05 .1 .15 .2 .25 remix - fade 0 4 .1 norm -1
+   play -n synth pl G2 pl B2 pl D3 pl G3 pl D4 pl G4 \\
+	delay 0 .05 .1 .15 .2 .25 remix - fade 0 4 .1 norm -1
 .EE
 .TP
 \fBdither\fR [\fB\-a\fR] [\fB\-S\fR\^|\^\fB\-s\fR\^|\^\fB\-f \fIfilter\fR]
--- a/src/effects_i.c
+++ b/src/effects_i.c
@@ -21,6 +21,7 @@
 #define LSX_EFF_ALIAS
 #include "sox_i.h"
 #include <string.h>
+#include <ctype.h>
 
 int lsx_usage(sox_effect_t * effp)
 {
@@ -290,9 +291,24 @@
   return 440 * pow(2., note / 12);
 }
 
+int lsx_parse_note(char const * text, char * * end_ptr)
+{
+  int result = INT_MAX;
+
+  if (*text >= 'A' && *text <= 'G') {
+    result = (int)(5/3. * (*text++ - 'A') + 9.5) % 12 - 9;
+    if (*text == 'b') {--result; ++text;}
+    else if (*text == '#') {++result; ++text;}
+    if (isdigit(*text))
+      result += 12 * (*text++ - '4'); 
+  }
+  *end_ptr = (char *)text;
+  return result;
+}
+
 /* Read string 'text' and convert to frequency.
  * 'text' can be a positive number which is the frequency in Hz.
- * If 'text' starts with a hash '%' and a following number the corresponding
+ * If 'text' starts with a '%' and a following number the corresponding
  * note is calculated.
  * Return -1 on error.
  */
@@ -305,6 +321,10 @@
     if (*end_ptr == text + 1)
       return -1;
     return calc_note_freq(result, key);
+  }
+  if (*text >= 'A' && *text <= 'G') {
+    int result = lsx_parse_note(text, end_ptr);
+    return result == INT_MAX? - 1 : calc_note_freq((double)result, key);
   }
   result = strtod(text, end_ptr);
   if (end_ptr) {
--- a/src/sox.c
+++ b/src/sox.c
@@ -2091,7 +2091,7 @@
 
 static int enum_option(int option_index, lsx_enum_item const * items)
 {
-  lsx_enum_item const * p = lsx_find_enum_text(lsx_optarg, items);
+  lsx_enum_item const * p = lsx_find_enum_text(lsx_optarg, items, 0);
   if (p == NULL) {
     size_t len = 1;
     char * set = lsx_malloc(len);
--- a/src/sox.h
+++ b/src/sox.h
@@ -565,7 +565,8 @@
 typedef struct {char const *text; unsigned value;} lsx_enum_item;
 #define LSX_ENUM_ITEM(prefix, item) {#item, prefix##item},
 
-lsx_enum_item const * lsx_find_enum_text(char const * text, lsx_enum_item const * lsx_enum_items);
+lsx_enum_item const * lsx_find_enum_text(char const * text, lsx_enum_item const * lsx_enum_items, unsigned flags);
+#define LSX_FET_CASE 1
 lsx_enum_item const * lsx_find_enum_value(unsigned value, lsx_enum_item const * lsx_enum_items);
 int lsx_enum_option(int c, lsx_enum_item const * items);
 sox_bool lsx_strends(char const * str, char const * end);
--- a/src/sox_i.h
+++ b/src/sox_i.h
@@ -82,6 +82,7 @@
     double max,         /* Maximum value on the y-axis. (e.g. +1) */
     double phase);      /* Phase at 1st point; 0..2pi. (e.g. pi/2 for cosine) */
 char const * lsx_parsesamples(sox_rate_t rate, const char *str, size_t *samples, int def);
+int lsx_parse_note(char const * text, char * * end_ptr);
 double lsx_parse_frequency_k(char const * text, char * * end_ptr, int key);
 #define lsx_parse_frequency(a, b) lsx_parse_frequency_k(a, b, INT_MAX)
 FILE * lsx_open_input_file(sox_effect_t * effp, char const * filename);
@@ -283,7 +284,7 @@
 #define TEXTUAL_PARAMETER(name, enum_table) { \
   lsx_enum_item const * e; \
   if (argc == 0) break; \
-  e = lsx_find_enum_text(*argv, enum_table); \
+  e = lsx_find_enum_text(*argv, enum_table, 0); \
   if (e != NULL) { \
     p->name = e->value; \
     --argc, ++argv; \
--- a/src/synth.c
+++ b/src/synth.c
@@ -6,6 +6,25 @@
  * This source code is freely redistributable and may be used for any purpose.
  * This copyright notice must be maintained.  The authors are not responsible
  * for the consequences of using this software.
+ *
+ * Except for synth types: pluck, tpdf, & brownnoise, and sweep types: linear
+ *   square & exp, which are:
+ *
+ * Copyright (c) 2006-2009 robs@users.sourceforge.net
+ *
+ * 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.1 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,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  */
 
 #include "sox_i.h"
@@ -152,12 +171,12 @@
   double p1, p2, p3; /* Use depends on synth type */
 
   /* internal stuff */
-  double cycle_start_time_s;
-  double last_out;
+  double lp_last_out, hp_last_out, hp_last_in, ap_last_out, ap_last_in;
+  double cycle_start_time_s, c0, c1, c2, c3, c4;
   PinkNoise pink_noise;
 
   double * buffer;
-  size_t buffer_len, pos, dx, dy, acc;
+  size_t buffer_len, pos;
 } channel_t;
 
 
@@ -208,12 +227,12 @@
         chan->p1 = 0.1;
         chan->p2 = 0.5;
         chan->p3 = 0.6;
-      } else if (chan->p2 < 0) { /* try a symetric waveform */
+      } else if (chan->p2 < 0) { /* try a symmetric waveform */
         if (chan->p1 <= 0.5) {
           chan->p2 = (1 - 2 * chan->p1) / 2;
           chan->p3 = chan->p2 + chan->p1;
         } else {
-          /* symetric is not possible, fall back to asymetrical triangle */
+          /* symetric is not possible, fall back to asymmetrical triangle */
           chan->p2 = chan->p1;
           chan->p3 = 1;
         }
@@ -237,7 +256,7 @@
       if (chan->p1 < 0)
         chan->p1 = .4;
       if (chan->p2 < 0)
-        chan->p2 = 0;
+        chan->p2 = .2, chan->p3 = .9;
 
     default: break;
   }
@@ -267,12 +286,15 @@
   priv_t * p = (priv_t *) effp->priv;
   channel_t master, * chan = &master;
   int key = INT_MAX, argn = 0;
-  char dummy;
+  char dummy, * end_ptr;
   --argc, ++argv;
 
   if (argc && !strcmp(*argv, "-n")) p->no_headroom = sox_true, ++argv, --argc;
 
-  if (argc > 1 && !strcmp(*argv, "-j") && sscanf(argv[1], "%i %c", &key, &dummy) == 1) {
+  if (argc > 1 && !strcmp(*argv, "-j") && (
+        sscanf(argv[1], "%i %c", &key, &dummy) == 1 || (
+          (key = lsx_parse_note(argv[1], &end_ptr)) != INT_MAX &&
+          !*end_ptr))) {
     argc -= 2;
     argv += 2;
   }
@@ -299,8 +321,7 @@
   }
 
   while (argn < argc) { /* type [combine] [f1[-f2] [off [ph [p1 [p2 [p3]]]]]] */
-    char * end_ptr;
-    lsx_enum_item const *enum_p = lsx_find_enum_text(argv[argn], synth_type);
+    lsx_enum_item const * enum_p = lsx_find_enum_text(argv[argn], synth_type, LSX_FET_CASE);
 
     if (enum_p == NULL) {
       lsx_fail("no type given");
@@ -314,7 +335,7 @@
       break;
 
     /* maybe there is a combine-type in next arg */
-    enum_p = lsx_find_enum_text(argv[argn], combine_type);
+    enum_p = lsx_find_enum_text(argv[argn], combine_type, LSX_FET_CASE);
     if (enum_p != NULL) {
       chan->combine = enum_p->value;
       if (++argn == argc)
@@ -322,16 +343,21 @@
     }
 
     /* read frequencies if given */
-    if (isdigit((int) argv[argn][0]) ||
-        argv[argn][0] == '.' || argv[argn][0] == '%') {
+    if (!lsx_find_enum_text(argv[argn], synth_type, LSX_FET_CASE) &&
+        argv[argn][0] != '-') {
       static const char sweeps[] = ":+/-";
 
       chan->freq2 = chan->freq = lsx_parse_frequency_k(argv[argn], &end_ptr, key);
-      if (chan->freq < 0) {
+      if (chan->freq < (chan->type == synth_pluck? 27.5 : 0) ||
+          (chan->type == synth_pluck && chan->freq > 4220)) {
         lsx_fail("invalid freq");
         return SOX_EOF;
       }
       if (*end_ptr && strchr(sweeps, *end_ptr)) {         /* freq2 given? */
+        if (chan->type >= synth_noise) {
+          lsx_fail("can't sweep this type");
+          return SOX_EOF;
+        }
         chan->sweep = strchr(sweeps, *end_ptr) - sweeps;
         chan->freq2 = lsx_parse_frequency_k(end_ptr + 1, &end_ptr, key);
         if (chan->freq2 < 0) {
@@ -384,7 +410,7 @@
 static int start(sox_effect_t * effp)
 {
   priv_t * p = (priv_t *)effp->priv;
-  size_t i, j;
+  size_t i, j, k;
 
   p->samples_done = 0;
 
@@ -399,24 +425,64 @@
     *chan = p->getopts_channels[i % p->getopts_nchannels];
     set_default_parameters(chan, i);
     if (chan->type == synth_pluck) {
-      int32_t r = 0;
-      double colour = pow(2., 4 * (chan->p2 - 1));
-      double dc = 0, a = 6.9 / (chan->p2 < .25? chan->p2  / .25 * 11 + 7 :
-                     chan->p2 <= .55? 18 :  (1-chan->p2) / .45 * 3 + 15);
-      chan->buffer_len = effp->in_signal.rate / chan->freq + .5;
-      chan->dx = chan->freq * 1000 + .5;
-      chan->dy = effp->in_signal.rate / chan->buffer_len * 1000 + .5;
-      chan->pos = chan->acc = 0;
-      chan->buffer = malloc(chan->buffer_len * sizeof(*chan->buffer));
-      chan->buffer[0] = 0;
-      for (j = 1; j < chan->buffer_len; dc += chan->buffer[j++])
-        do chan->buffer[j] =
-            chan->buffer[j - 1] + (chan->p3 == 0? DRANQD1:dranqd1(r)) * colour;
-        while (fabs(chan->buffer[j]) > 1);
-      for (dc /= chan->buffer_len, j = 0; j < chan->buffer_len; ++j)
-        chan->buffer[j] = range_limit(chan->buffer[j] - dc, -1, 1) * a;
-      chan->p3 = .5 * exp(log(.6) / chan->freq / chan->p1);
-      lsx_debug("a=%g colour=%g", a, 1/colour);
+      double min, max, frac, p2;
+      double const decay_rate = -2; /* dB / s */
+      double const decay_f = min(912, 266 + 106 * log(chan->freq));
+      double d = sqr(dB_to_linear(decay_rate / chan->freq));
+      d = (d * cos(2 * M_PI * decay_f / effp->in_signal.rate) - 1) / (d - 1);
+      chan->c0 = d - sqrt(d * d - 1);
+      chan->c1 = 1 - chan->c0;
+
+      chan->c1 *= exp(-2e4/ (.05+chan->p1)/ chan->freq/ effp->in_signal.rate);
+
+      chan->c2 = exp(-2 * M_PI * 10 / effp->in_signal.rate);
+      chan->c3 = (1 + chan->c2) * .5;
+
+      if (effp->in_signal.rate < 44100 || effp->in_signal.rate > 48000) {
+        lsx_fail("sample rate for pluck must be 44100-48000; use `rate' to resample");
+        return SOX_EOF;
+      }
+      d = chan->c0 / (chan->c0 + chan->c1);
+
+      chan->buffer_len = effp->in_signal.rate / chan->freq - d;
+      frac = effp->in_signal.rate / chan->freq - d - chan->buffer_len;
+      chan->c4 = (1 - frac) / (1 + frac);
+      chan->pos = 0;
+
+      chan->buffer = lsx_calloc(chan->buffer_len, sizeof(*chan->buffer));
+      for (k = 0, p2 = chan->p2; k < 2 && p2 >= 0; ++k, p2 = chan->p3) {
+        double d1 = 0, d, colour = pow(2., 4 * (p2 - 1));
+        int32_t r = p2 * 100 + .5;
+        for (j = 0; j < chan->buffer_len; ++j) {
+          do d = d1 + (chan->phase? DRANQD1:dranqd1(r)) * colour;
+          while (fabs(d) > 1);
+          chan->buffer[j] += d * (1 - .3 * k);
+          d1 = d * (colour != 1);
+#ifdef TEST_PLUCK
+          chan->buffer[j] = sin(2 * M_PI * j / chan->buffer_len);
+#endif
+        }
+      }
+      for (j = 0, min = max = 0; j < chan->buffer_len; ++j) {
+        double d, t = (double)j / chan->buffer_len;
+        chan->lp_last_out = d =
+          chan->buffer[j] * chan->c1 + chan->lp_last_out * chan->c0;
+
+          chan->ap_last_out =
+            d * chan->c4 + chan->ap_last_in - chan->ap_last_out * chan->c4;
+          chan->ap_last_in = d;
+
+        chan->buffer[j] = chan->buffer[j] * (1 - t) + chan->ap_last_out * t;
+        min = min(min, chan->buffer[j]);
+        max = max(max, chan->buffer[j]);
+      }
+      for (j = 0, d = 0; j < chan->buffer_len; ++j) {
+        chan->buffer[j] = (2 * chan->buffer[j] - max - min) / (max - min);
+        d += sqr(chan->buffer[j]);
+      }
+      lsx_debug("rms=%f c0=%f c1=%f c2=%f c3=%f df=%f d3f=%f",
+          10 * log(d / chan->buffer_len), chan->c0, chan->c1, chan->c2,
+          chan->c3, decay_f, log(chan->c0)/ -2 / M_PI * effp->in_signal.rate);
     }
     switch (chan->sweep) {
       case Linear: chan->mult = p->samples_to_do?
@@ -587,22 +653,27 @@
           break;
 
         case synth_brownnoise:
-          do synth_out = chan->last_out + DRANQD1 * (1. / 16);
+          do synth_out = chan->lp_last_out + DRANQD1 * (1. / 16);
           while (fabs(synth_out) > 1);
-          chan->last_out = synth_out;
+          chan->lp_last_out = synth_out;
           break;
 
         case synth_pluck: {
-          size_t pos1 = chan->pos + 1 == chan->buffer_len? 0 : chan->pos + 1;
-          double t = (double)chan->acc / chan->dy;
-          synth_out = chan->buffer[chan->pos] * (1-t) + chan->buffer[pos1] * t;
-          for (chan->acc+=chan->dx; chan->acc>=chan->dy; chan->acc-=chan->dy) {
-            t = chan->buffer[chan->pos];
-            chan->buffer[chan->pos] =
-              chan->p3 * (chan->buffer[chan->pos] + chan->last_out);
-            chan->last_out = t;
-            chan->pos = chan->pos + 1 == chan->buffer_len? 0 : chan->pos + 1;
-          }
+          double d = chan->buffer[chan->pos];
+
+          chan->hp_last_out = 
+             (d - chan->hp_last_in) * chan->c3 + chan->hp_last_out * chan->c2;
+          chan->hp_last_in = d;
+        
+          synth_out = range_limit(chan->hp_last_out, -1, 1);
+
+          chan->lp_last_out = d = d * chan->c1 + chan->lp_last_out * chan->c0;
+
+          chan->ap_last_out = chan->buffer[chan->pos] =
+            (d - chan->ap_last_out) * chan->c4 + chan->ap_last_in;
+          chan->ap_last_in = d;
+
+          chan->pos = chan->pos + 1 == chan->buffer_len? 0 : chan->pos + 1;
           break;
         }
 
--- a/src/util.c
+++ b/src/util.c
@@ -58,14 +58,17 @@
   return result;
 }
 
-lsx_enum_item const * lsx_find_enum_text(char const * text, lsx_enum_item const * enum_items)
+lsx_enum_item const * lsx_find_enum_text(char const * text, lsx_enum_item const * enum_items, unsigned flags)
 {
   lsx_enum_item const * result = NULL; /* Assume not found */
+  sox_bool sensitive = !!(flags & LSX_FET_CASE);
 
   while (enum_items->text) {
-    if (strcasecmp(text, enum_items->text) == 0)
+    if ((!sensitive && !strcasecmp(text, enum_items->text)) ||
+        ( sensitive && !    strcmp(text, enum_items->text)))
       return enum_items;    /* Found exact match */
-    if (strncasecmp(text, enum_items->text, strlen(text)) == 0) {
+    if ((!sensitive && !strncasecmp(text, enum_items->text, strlen(text))) ||
+        ( sensitive && !    strncmp(text, enum_items->text, strlen(text)))) {
       if (result != NULL && result->value != enum_items->value)
         return NULL;        /* Found ambiguity */
       result = enum_items;  /* Found sub-string match */
@@ -85,7 +88,7 @@
 
 int lsx_enum_option(int c, lsx_enum_item const * items)
 {
-  lsx_enum_item const * p = lsx_find_enum_text(lsx_optarg, items);
+  lsx_enum_item const * p = lsx_find_enum_text(lsx_optarg, items, sox_false);
   if (p == NULL) {
     size_t len = 1;
     char * set = lsx_malloc(len);
--- a/src/vol.c
+++ b/src/vol.c
@@ -60,7 +60,7 @@
   }
 
   if (have_type) {
-    lsx_enum_item const * p = lsx_find_enum_text(type_ptr, vol_types);
+    lsx_enum_item const * p = lsx_find_enum_text(type_ptr, vol_types, 0);
     if (!p)
       return lsx_usage(effp);
     switch (p->value) {