ref: 63536e3fe121f732ec3d73bc135ff52ebf78e58a
parent: f0724fe5fc30fc597c235b5d6f59e478b10cba0d
author: robs <robs>
date: Sat Jan 20 16:57:40 EST 2007
Added allpass effect; consolidation & usability enhancements of other filters.
--- a/ChangeLog
+++ b/ChangeLog
@@ -28,6 +28,9 @@
o bass and treble altering effects. (robs)
o Documented the butterworth filter effects; added variable Q. (robs)
o Visualisation of various filters' frequency response via Octave. (robs)
+ o Added allpass filter effect. (robs)
+ o Can now specify width of many 2nd-order filters as: Hz, octaves,
+ or Q. (robs)
o Fix rounding error when reading command-line time
parameters. (robs)
o Allow command-line time parameters of < 1 sec to omit the
--- a/sox.1
+++ b/sox.1
@@ -1015,8 +1015,18 @@
and angle brackets < > to denote those that are repeatable but not
optional.
.TP
+\fBallpass\fR \fIfrequency width\fR[\fBh\fR\^|\^\fBo\fR\^|\^\fBq\fR]
+Apply a two-pole all-pass filter with central frequency (in Hz)
+\fIfrequency\fR, and filter-width \fIwidth\fR: in Hz (the default, or if
+appended with `\fBh\fR'), in octaves (if appended with `\fBo\fR'), or as
+a Q-factor (if appended with `\fBq\fR'). An all-pass filter changes the
+audio's frequency to phase relationship without changing its frequency
+to amplitude relationship. The filter is described in detail in [1].
+.SP
+This effect supports the \fB\-\-octave\fR global option.
+.TP
\fBavg\fR [ \fB\-l\fR\^|\^\fB\-r\fR\^|\^\fB\-f\fR\^|\^\fB\-b\fR\^|\^\fB\-1\fR\^|\^\fB\-2\fR\^|\^\fB\-3\fR\^|\^\fB\-4\fR\^|\^\fIn\fR{\fB,\fIn\fR} ]
-Reduce the number of channels by averaging the samples,
+Reduce the number of audio channels by mixing or selecting channels,
or duplicate channels to increase the number of channels.
This effect is automatically used when the number of input
channels differ from the number of output channels. When reducing
@@ -1061,7 +1071,7 @@
.TE
.SP
.TP
-\fBband\fR [\fB\-n\fR] \fIcenter\fR [\fIwidth\fR]
+\fBband\fR [\fB\-n\fR] \fIcenter\fR [width\fR[\fBh\fR\^|\^\fBo\fR\^|\^\fBq\fR]]
Apply a band-pass filter.
The frequency response drops logarithmically
around the
@@ -1069,6 +1079,8 @@
frequency.
The
.I width
+in Hz (the default, or if appended with `\fBh\fR'), in octaves (if
+appended with `\fBo\fR'), or as a Q-factor (if appended with `\fBq\fR'),
gives the slope of the drop.
The frequencies at
.I center
@@ -1093,24 +1105,33 @@
.I center
frequency and settling around it.
.SP
-This effect supports the \fB\-\-octave\fR global option (see above).
+This effect supports the \fB\-\-octave\fR global option.
.SP
See also \fBfilter\fR for a bandpass filter with steeper shoulders.
.TP
-\fBbandpass\fR\^|\^\fBbandreject \fIfrequency bandwidth\fR
+\fBbandpass\fR\^|\^\fBbandreject\fR [\fB\-c\fR] \fIfrequency width\fR[\fBh\fR\^|\^\fBo\fR\^|\^\fBq\fR]
Apply a two-pole Butterworth band-pass or band-reject filter with
-central frequency (in Hz) \fIfrequency\fR,
-and bandwidth (in Hz, and as determined by the 3dB points)
-\fIbandwidth\fR.
-The filter rolls off at 6dB per octave (20dB per decade).
+central frequency (in Hz) \fIfrequency\fR, and (3dB-point) band-width
+\fIwidth\fR: in Hz (the default, or if appended with `\fBh\fR'), in
+octaves (if appended with `\fBo\fR'), or as a Q-factor (if appended with
+`\fBq\fR'). The
+.B \-c
+option applies only to
+.B bandpass
+and selects a constant skirt gain (peak gain = Q) instead of the
+default: constant 0dB peak gain.
+The filters roll off at 6dB per octave (20dB per decade)
+and are described in detail in [1].
.SP
-These effects support the \fB\-\-octave\fR global option (see above).
+These effects support the \fB\-\-octave\fR global option.
+.SP
+See also \fBfilter\fR for a bandpass filter with steeper shoulders.
.TP
-\fBbandreject \fIfrequency bandwidth\fR
+\fBbandreject \fIfrequency width\fR[\fBh\fR\^|\^\fBo\fR\^|\^\fBq\fR]
Apply a band-reject filter.
See the description of the \fBbandpass\fR effect for details.
.TP
-\fBbass\fR\^|\^\fBtreble \fIgain\fR [\fIfrequency\fR] [\fIslope\fR]
+\fBbass\fR\^|\^\fBtreble \fIgain\fR [\fIfrequency\fR [\fIwidth\fR[\fBs\fR\^|\^\fBh\fR\^|\^\fBo\fR\^|\^\fBq\fR]]]
Boost or cut the bass (lower) or treble (upper) frequencies of the audio
using a two-pole shelving filter with a response similar to that
of a standard hi-fi's (Baxandall) tone-controls. This is also
@@ -1125,20 +1146,28 @@
when using a positive \fIgain\fR.
.SP
If desired, the filter can be fine-tuned using the following
-optional parameters (in either order):
+optional parameters:
.SP
-\fIfrequency\fR sets the filter's center frequency and so can be
+\fIfrequency\fR sets the filter's central frequency and so can be
used to extend or reduce the frequency range to be boosted or
cut. The default value is 100Hz (for \fBbass\fR) or 3kHz (for
\fBtreble\fR).
.SP
-\fIslope\fR is a number between 0 and 1 that determines how
-steep the filter's shelf transition is. Its useful range is
-about 0\*d3 (for a gentle slope) to 1 (for a steep slope). The
+\fIwidth\fR
+determines how
+steep the filter's shelf transition is and can be expressed as:
+a `slope' (the default, or if appended with `\fBs\fR'),
+a Q-factor (if appended with `\fBq\fR'),
+the transition width in octaves (if appended with `\fBo\fR'),
+or the transition width in Hz (if appended with `\fBh\fR').
+The useful range of `slope' is
+about 0\*d3, for a gentle slope, to 1 (the maximum), for a steep slope; the
default value is 0\*d5.
.SP
-These effects support the \fB\-\-octave\fR global option (see above).
+The filters are described in detail in [1].
.SP
+These effects support the \fB\-\-octave\fR global option.
+.SP
See also \fBequalizer\fR for a peaking equalisation effect.
.TP
\fBchorus \fIgain-in gain-out\fR <\fIdelay decay speed depth \fB\-s\fR\^|\^\fB\-t\fR>
@@ -1207,11 +1236,12 @@
\fBdeemph\fR
Apply a treble attenuation shelving filter to audio in
audio-CD format. The frequency response of pre-emphasized
-recordings is rectified. The filtering is defined in the
+recordings is rectified. The filter is defined in the
standard document ISO 908.
.SP
-This effect supports the \fB\-\-octave\fR global option (see above).
+This effect supports the \fB\-\-octave\fR global option.
.SP
+See also the \fBbass\fR and \fBtreble\fR shelving equalisation effects.
.TP
\fBdither\fR [\fIdepth\fR]
Apply dithering to the audio.
@@ -1251,15 +1281,18 @@
and the decay (relative to gain-in) of that echo.
Gain-out is the volume of the output.
.TP
-\fBequalizer \fIfrequency Q gain\fR
+\fBequalizer \fIfrequency width\fR[\fBq\fR\^|\^\fBo\fR\^|\^\fBh\fR] \fIgain\fR
Apply a two-pole peaking equalisation (EQ) filter.
With this filter, the signal-level at and around a selected frequency
can be increased or decreased, whilst (unlike band-pass and band-reject
filters) that at all other frequencies is unchanged.
.SP
-\fIfrequency\fR gives the filter's central frequency in Hz, \fIQ\fR its
-band-width specified as a `Q-factor' (see
-http://en.wikipedia.org/wiki/Q_factor), and \fIgain\fR the required gain
+\fIfrequency\fR gives the filter's central frequency in Hz,
+\fIwidth\fR, the band-width,
+as a Q-factor [2] (the default, or if appended with `\fBq\fR'),
+in octaves (if appended with `\fBo\fR'),
+or in Hz (if appended with `\fBh\fR'),
+and \fIgain\fR the required gain
or attenuation in dB.
Beware of
.B Clipping
@@ -1268,8 +1301,10 @@
In order to produce complex equalisation curves, this effect
can be given several times, each with a different central frequency.
.SP
-This effect supports the \fB\-\-octave\fR global option (see above).
+The filter is described in detail in [1].
.SP
+This effect supports the \fB\-\-octave\fR global option.
+.SP
See also \fBbass\fR and \fBtreble\fR for shelving equalisation effects.
.TP
\fBfade\fR [\fItype\fR] \fIfade-in-length\fR [\fIstop-time\fR [\fIfade-out-length\fR] ]
@@ -1345,6 +1380,7 @@
T}
.TE
.SP
+See [3] for a detailed description of flanging.
.TP
\fBhighp\fR \fIfrequency\fR
Apply a high-pass filter.
@@ -1354,23 +1390,23 @@
.B -1
option; it is retained for backwards compatibilty only.
.TP
-\fBhighpass\fR\^|\^\fBlowpass\fR [\fB-1\fR|\fB-2\fR] \fIfrequency\fR [\fIQ\fR]
+\fBhighpass\fR\^|\^\fBlowpass\fR [\fB-1\fR|\fB-2\fR] \fIfrequency\fR [\fRwidth\fR[\fBq\fR\^|\^\fBo\fR\^|\^\fBh\fR]]
Apply a high-pass or low-pass filter with 3dB point \fIfrequency\fR.
The filter can be either single-pole (with
.BR -1 ),
-or double-pole (with
-.B -2
-or if unspecified).
-.I Q
-applies only to double-pole filters; its default value is 0\*d707 and gives
-a Butterworth response. The filters roll off at
-6dB per pole per octave (20dB per pole per decade). See
-http://musicdsp.org/files/Audio-EQ-Cookbook.txt for a detailed
-description of the double-pole filters.
+or double-pole (the default, or with
+.BR -2 ).
+.I width
+applies only to double-pole filters and is the filter-width: as a
+Q-factor (the default, or if appended with `\fBq\fR'), in octaves (if
+appended with `\fBo\fR'), or in Hz (if appended with `\fBh\fR');
+the default Q is 0\*d707 and gives a Butterworth response. The filters
+roll off at 6dB per pole per octave (20dB per pole per decade). The
+double-pole filters are described in detail in [1].
.SP
-These effects support the \fB\-\-octave\fR global option (see above).
+These effects support the \fB\-\-octave\fR global option.
.SP
-See also \fBfilter\fR for filters with a sharper cutoff.
+See also \fBfilter\fR for filters with a steeper roll-off.
.TP
\fBlowp \fIfrequency\fR
Apply a low-pass filter.
@@ -1906,7 +1942,7 @@
\fIp3\fR (trapezium): the percentage through each cycle at which `falling'
ends; default=60.
.TP
-\fBtreble \fIgain\fR [\fIfrequency\fR] [\fIslope\fR]
+\fBtreble \fIgain\fR [\fIfrequency\fR [\fIwidth\fR[\fBs\fR\^|\^\fBh\fR\^|\^\fBo\fR\^|\^\fBq\fR]]]
Apply a treble tone-control effect.
See the description of the \fBbass\fR effect for details.
.TP
@@ -1978,7 +2014,7 @@
less than 0 decreases it,
and greater than 0 increases it.
.SP
-See http://en.wikipedia.org/wiki/Decibel
+See [4]
for a detailed discussion on electrical (and hence audio signal)
voltage and power ratios.
.SP
@@ -2004,6 +2040,27 @@
.BR libst (3)
.SP
The SoX web page at http://sox.sourceforge.net
+.SS References
+.TP
+[1]
+R. Bristow-Johnson,
+.IR "Cookbook formulae for audio EQ biquad filter coefficients" ,
+http://musicdsp.org/files/Audio-EQ-Cookbook.txt
+.TP
+[2]
+Wikipedia,
+.IR "Q-factor" ,
+http://en.wikipedia.org/wiki/Q_factor
+.TP
+[3]
+Scott Lehman,
+.IR "Flanging" ,
+http://harmony-central.com/Effects/Articles/Flanging
+.TP
+[4]
+Wikipedia,
+.IR "Decibel" ,
+http://en.wikipedia.org/wiki/Decibel
.SH LICENSE
Copyright 1991 Lance Norskog and Sundry Contributors.
Copyright 1998\-2007 by Chris Bagwell and SoX Contributors.
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -22,14 +22,12 @@
sfircam.h skelform.c smp.c sndfile.c sndrtool.c sphere.c tx16w.c \
voc.c vorbis.c vox.c wav.c wav.h wve.c xa.c
-effects = avg.c band.c bandpass.c biquad.c biquad.h biquadlh.c breject.c \
- btrworth.c btrworth.h chorus.c compand.c dcshift.c deemphas.c \
- earwax.c echo.c echos.c equalizer.c fade.c FFT.c FFT.h filter.c \
- flanger.c luaeff.c luaform.c lintlib.c mask.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 skeleff.c speed.c stat.c stretch.c swap.c synth.c tone.c \
- trim.c vibro.c vol.c
+effects = avg.c band.h biquad.c biquad.h biquads.c chorus.c compand.c dcshift.c\
+ deemphas.c earwax.c echo.c echos.c fade.c FFT.c FFT.h filter.c \
+ flanger.c luaeff.c luaform.c lintlib.c mask.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 \
+ skeleff.c speed.c stat.c stretch.c swap.c synth.c trim.c vibro.c vol.c
libst_la_SOURCES = $(formats) $(effects) alsa.c oss.c sunaudio.c handlers.c \
misc.c st_i.h stio.c stlua.c util.c xmalloc.c xmalloc.h \
--- a/src/README
+++ b/src/README
@@ -4,10 +4,11 @@
2nd column: could clip (in theory)
3rd column: CLIP_COUNT reporting implemented: yes, no, or (yes) custom
+allpass y y
avg y y
-band n n
-bandpass n y
-bandreject see bandpass
+band y y
+bandpass y y
+bandreject y y
bass y y
chorus y y
compand y y
@@ -17,13 +18,13 @@
earwax ? n
echos y y
echo y y
-equalizer see bass
+equalizer y y
fade n n
filter n n
flanger y y
-highpass see bandpass
+highpass y y
highp n y
-lowpass see bandpass
+lowpass n y
lowp n y
lua y n TODO
mask y y
@@ -48,7 +49,7 @@
stretch y y
swap n n
synth n n
-treble see bass
+treble y y
trim n n
vibro n n
vol y y
--- a/src/band.c
+++ /dev/null
@@ -1,185 +1,0 @@
-/*
- * July 5, 1991
- * Copyright 1991 Lance Norskog And Sundry Contributors
- * This source code is freely redistributable and may be used for
- * any purpose. This copyright notice must be maintained.
- * Lance Norskog And Sundry Contributors are not responsible for
- * the consequences of using this software.
- */
-
-/*
- * Sound Tools Bandpass effect file.
- *
- * Algorithm: 2nd order recursive filter.
- * Formula stolen from MUSIC56K, a toolkit of 56000 assembler stuff.
- * Quote:
- * This is a 2nd order recursive band pass filter of the form.
- * y(n)= a * x(n) - b * y(n-1) - c * y(n-2)
- * where :
- * x(n) = "IN"
- * "OUT" = y(n)
- * c = EXP(-2*pi*cBW/S_RATE)
- * b = -4*c/(1+c)*COS(2*pi*cCF/S_RATE)
- * if cSCL=2 (i.e. noise input)
- * a = SQT(((1+c)*(1+c)-b*b)*(1-c)/(1+c))
- * else
- * a = SQT(1-b*b/(4*c))*(1-c)
- * endif
- * note : cCF is the center frequency in Hertz
- * cBW is the band width in Hertz
- * cSCL is a scale factor, use 1 for pitched sounds
- * use 2 for noise.
- *
- *
- * July 1, 1999 - Jan Paul Schmidt <jps@fundament.org>
- *
- * This looks like the resonator band pass in SPKit. It's a
- * second order all-pole (IIR) band-pass filter described
- * at the pages 186 - 189 in
- * Dodge, Charles & Jerse, Thomas A. 1985:
- * Computer Music -- Synthesis, Composition and Performance.
- * New York: Schirmer Books.
- * Reference from the SPKit manual.
- */
-
-#include <math.h>
-#include <string.h>
-#include "st_i.h"
-
-static st_effect_t st_band_effect;
-
-/* Private data for Bandpass effect */
-typedef struct bandstuff {
- float center;
- float width;
- double A, B, C;
- double out1, out2;
- short noise;
- /* 50 bytes of data, 52 bytes long for allocation purposes. */
-} *band_t;
-
-/*
- * Process options
- */
-static int st_band_getopts(eff_t effp, int n, char **argv)
-{
- band_t band = (band_t) effp->priv;
-
- band->noise = 0;
- if (n > 0 && !strcmp(argv[0], "-n")) {
- band->noise = 1;
- n--;
- argv++;
- }
- if ((n < 1) || !sscanf(argv[0], "%f", &band->center))
- {
- st_fail(st_band_effect.usage);
- return (ST_EOF);
- }
- band->width = band->center / 2;
- if ((n >= 2) && !sscanf(argv[1], "%f", &band->width))
- {
- st_fail(st_band_effect.usage);
- return (ST_EOF);
- }
- return (ST_SUCCESS);
-}
-
-/*
- * Prepare processing.
- */
-static int st_band_start(eff_t effp)
-{
- band_t band = (band_t) effp->priv;
- if (band->center > effp->ininfo.rate/2)
- {
- st_fail("Band: center must be < minimum data rate/2");
- return (ST_EOF);
- }
-
- band->C = exp(-2*M_PI*band->width/effp->ininfo.rate);
- band->B = -4*band->C/(1+band->C)*
- cos(2*M_PI*band->center/effp->ininfo.rate);
- if (band->noise)
- band->A = sqrt(((1+band->C)*(1+band->C)-band->B *
- band->B)*(1-band->C)/(1+band->C));
- else
- band->A = sqrt(1-band->B*band->B/(4*band->C))*(1-band->C);
- band->out1 = band->out2 = 0.0;
- if (effp->globalinfo->octave_plot_effect)
- {
- printf(
- "title('SoX effect: %s centre=%g width=%g (rate=%u)')\n"
- "xlabel('Frequency (Hz)')\n"
- "ylabel('Amplitude Response (dB)')\n"
- "Fs=%u;minF=10;maxF=Fs/2;\n"
- "axis([minF maxF -75 25])\n"
- "sweepF=logspace(log10(minF),log10(maxF),200);\n"
- "grid on\n"
- "[h,w]=freqz([%f 0 0],[1 %f %f],sweepF,Fs);\n"
- "semilogx(w,20*log10(h),'b')\n"
- "pause\n"
- , effp->name, band->center, band->width
- , effp->ininfo.rate, effp->ininfo.rate
- , band->A
- , band->B
- , band->C
- );
- return ST_EOF;
- }
- return (ST_SUCCESS);
-}
-
-/*
- * Processed signed long samples from ibuf to obuf.
- * Return number of samples processed.
- */
-
-static int st_band_flow(eff_t effp, const st_sample_t *ibuf, st_sample_t *obuf,
- st_size_t *isamp, st_size_t *osamp)
-{
- band_t band = (band_t) effp->priv;
- int len, done;
- double d;
- st_sample_t l;
-
- len = ((*isamp > *osamp) ? *osamp : *isamp);
-
- /* yeah yeah yeah registers & integer arithmetic yeah yeah yeah */
- for(done = 0; done < len; done++) {
- l = *ibuf++;
- d = (band->A * l - band->B * band->out1) - band->C * band->out2;
- band->out2 = band->out1;
- band->out1 = d;
- *obuf++ = d;
- }
- *isamp = len;
- *osamp = len;
- return(ST_SUCCESS);
-}
-
-/*
- * Do anything required when you stop reading samples.
- * Don't close input file!
- */
-static int st_band_stop(eff_t effp UNUSED)
-{
- return (ST_SUCCESS); /* nothing to do */
-}
-
-static st_effect_t st_band_effect = {
- "band",
- "Usage: band [ -n ] center [ width ]",
- 0,
- st_band_getopts,
- st_band_start,
- st_band_flow,
- st_effect_nothing_drain,
- st_band_stop,
- st_effect_nothing
-};
-
-const st_effect_t *st_band_effect_fn(void)
-{
- return &st_band_effect;
-}
--- /dev/null
+++ b/src/band.h
@@ -1,0 +1,50 @@
+/*
+ * July 5, 1991
+ * Copyright 1991 Lance Norskog And Sundry Contributors
+ * This source code is freely redistributable and may be used for
+ * any purpose. This copyright notice must be maintained.
+ * Lance Norskog And Sundry Contributors are not responsible for
+ * the consequences of using this software.
+ */
+
+/*
+ * Sound Tools Bandpass effect file.
+ *
+ * Algorithm: 2nd order recursive filter.
+ * Formula stolen from MUSIC56K, a toolkit of 56000 assembler stuff.
+ * Quote:
+ * This is a 2nd order recursive band pass filter of the form.
+ * y(n)= a * x(n) - b * y(n-1) - c * y(n-2)
+ * where :
+ * x(n) = "IN"
+ * "OUT" = y(n)
+ * c = EXP(-2*pi*cBW/S_RATE)
+ * b = -4*c/(1+c)*COS(2*pi*cCF/S_RATE)
+ * if cSCL=2 (i.e. noise input)
+ * a = SQT(((1+c)*(1+c)-b*b)*(1-c)/(1+c))
+ * else
+ * a = SQT(1-b*b/(4*c))*(1-c)
+ * endif
+ * note : cCF is the center frequency in Hertz
+ * cBW is the band width in Hertz
+ * cSCL is a scale factor, use 1 for pitched sounds
+ * use 2 for noise.
+ *
+ *
+ * July 1, 1999 - Jan Paul Schmidt <jps@fundament.org>
+ *
+ * This looks like the resonator band pass in SPKit. It's a
+ * second order all-pole (IIR) band-pass filter described
+ * at the pages 186 - 189 in
+ * Dodge, Charles & Jerse, Thomas A. 1985:
+ * Computer Music -- Synthesis, Composition and Performance.
+ * New York: Schirmer Books.
+ * Reference from the SPKit manual.
+ */
+
+ p->a2 = exp(-2 * M_PI * bw_Hz / effp->ininfo.rate);
+ p->a1 = -4 * p->a2 / (1 + p->a2) * cos(2 * M_PI * p->fc / effp->ininfo.rate);
+ if (p->filter_type == filter_BPF_SPK_N)
+ p->b0 = sqrt(((1+p->a2) * (1+p->a2) - p->a1*p->a1) * (1-p->a2) / (1+p->a2));
+ else
+ p->b0 = sqrt(1 - p->a1 * p->a1 / (4 * p->a2)) * (1 - p->a2);
--- a/src/bandpass.c
+++ /dev/null
@@ -1,97 +1,0 @@
-/*
- Band-pass effect file for SoX
- Copyright (C) 1999 Jan Paul Schmidt <jps@fundament.org>
-
- 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, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
- Code based on the band-pass implementation in
-
- Sound Processing Kit - A C++ Class Library for Audio Signal Processing
- Copyright (C) 1995-1998 Kai Lassfolk
-
- as described in
-
- Computer music: synthesis, composition, and performance
- Charles Dodge, Thomas A. Jerse
- [2nd ed.]
- Page 216
-
- */
-
-#include <math.h>
-
-#include "st_i.h"
-#include "btrworth.h"
-
-static st_effect_t st_bandpass_effect;
-
-static int st_bandpass_getopts (eff_t effp, int n, char **argv)
-{
- butterworth_t butterworth = (butterworth_t)effp->priv;
-
- if (n != 2) {
- st_fail(st_bandpass_effect.usage);
- return (ST_EOF);
- }
-
- st_butterworth_start (effp);
-
- if (!(sscanf (argv [0], "%lf", &butterworth->frequency))) {
- st_fail("bandpass: illegal frequency");
- return (ST_EOF);
- }
-
- if (!(sscanf (argv [1], "%lf", &butterworth->bandwidth))) {
- st_fail("bandpass: illegal bandwidth");
- return (ST_EOF);
- }
- return (ST_SUCCESS);
-}
-
-static int st_bandpass_start(eff_t effp)
-{
- butterworth_t butterworth = (butterworth_t) effp->priv;
- double c;
- double d;
-
- c = 1.0 / tan (M_PI * butterworth->bandwidth / effp->ininfo.rate);
- d = 2 * cos (2 * M_PI * butterworth->frequency / effp->ininfo.rate);
-
- butterworth->a [0] = 1.0 / (1.0 + c);
- butterworth->a [1] = 0.0;
- butterworth->a [2] = -butterworth->a [0];
-
- butterworth->b [0] = -c * d * butterworth->a [0];
- butterworth->b [1] = (c - 1.0) * butterworth->a[0];
- st_butterworth_plot(effp);
- return (ST_SUCCESS);
-}
-
-static st_effect_t st_bandpass_effect = {
- "bandpass",
- "Usage: bandpass FREQUENCY BANDWIDTH",
- 0,
- st_bandpass_getopts,
- st_bandpass_start,
- st_butterworth_flow,
- st_effect_nothing_drain,
- st_effect_nothing,
- st_effect_nothing
-};
-
-const st_effect_t *st_bandpass_effect_fn(void)
-{
- return &st_bandpass_effect;
-}
--- a/src/biquad.c
+++ b/src/biquad.c
@@ -14,16 +14,51 @@
* Fifth Floor, 51 Franklin Street, Boston, MA 02111-1301, USA.
*/
-/* Biquad filter common functions (c) 2006 robs@users.sourceforge.net */
+/* Biquad filter common functions (c) 2006-7 robs@users.sourceforge.net */
+
#include "biquad.h"
+#include <string.h>
+static char const * const width_str[] = {
+ "band-width(Hz)",
+ "band-width(Hz, no warp)", /* deprecated */
+ "band-width(octaves)",
+ "Q",
+ "slope",
+};
+static char const all_width_types[] = "hboqs";
-int st_biquad_start(eff_t effp, char const * width_name)
+
+int st_biquad_getopts(eff_t effp, int n, char **argv,
+ int min_args, int max_args, int fc_pos, int width_pos, int gain_pos,
+ char const * allowed_width_types, filter_t filter_type)
{
biquad_t p = (biquad_t) effp->priv;
+ char width_type = *allowed_width_types;
+ char dummy; /* To check for extraneous chars. */
+ p->filter_type = filter_type;
+ if (n < min_args || n > max_args ||
+ (n > fc_pos && (sscanf(argv[fc_pos], "%lf %c", &p->fc, &dummy) != 1 || p->fc <= 0)) ||
+ (n > width_pos && ((unsigned)(sscanf(argv[width_pos], "%lf%c %c", &p->width, &width_type, &dummy)-1) > 1 || p->width <= 0)) ||
+ (n > gain_pos && sscanf(argv[gain_pos], "%lf %c", &p->gain, &dummy) != 1) ||
+ !strchr(allowed_width_types, width_type) || (width_type == 's' && p->width > 1)) {
+ st_fail(effp->h->usage);
+ return ST_EOF;
+ }
+ p->width_type = strchr(all_width_types, width_type) - all_width_types;
+ if (p->width_type >= strlen(all_width_types))
+ p->width_type = 0;
+ return ST_SUCCESS;
+}
+
+
+int st_biquad_start(eff_t effp)
+{
+ biquad_t p = (biquad_t) effp->priv;
+
/* Simplify: */
p->b2 = p->b2/p->a0;
p->b1 = p->b1/p->a0;
@@ -31,28 +66,19 @@
p->a2 = p->a2/p->a0;
p->a1 = p->a1/p->a0;
- if (p->dcNormalise) /* Normalise to AV = 0dB at DC: */
- {
- double normalise = (1 + p->a1 + p->a2) / (p->b0 + p->b1 + p->b2);
- p->b0 = normalise * p->b0;
- p->b1 = normalise * p->b1;
- p->b2 = normalise * p->b2;
- }
-
- if (effp->globalinfo->octave_plot_effect)
- {
+ if (effp->globalinfo->octave_plot_effect) {
printf(
- "title('SoX effect: %s gain=%g centre=%g %s=%g (rate=%u)')\n"
+ "title('SoX effect: %s gain=%g frequency=%g %s=%g (rate=%u)')\n"
"xlabel('Frequency (Hz)')\n"
"ylabel('Amplitude Response (dB)')\n"
"Fs=%u;minF=10;maxF=Fs/2;\n"
- "axis([minF maxF -25 25])\n"
+ "axis([minF maxF -35 25])\n"
"sweepF=logspace(log10(minF),log10(maxF),200);\n"
"grid on\n"
- "[h,w]=freqz([%f %f %f],[1 %f %f],sweepF,Fs);\n"
+ "[h,w]=freqz([%g %g %g],[1 %g %g],sweepF,Fs);\n"
"semilogx(w,20*log10(h),'b')\n"
"pause\n"
- , effp->name, p->gain, p->fc, width_name, p->width.q
+ , effp->name, p->gain, p->fc, width_str[p->width_type], p->width
, effp->ininfo.rate, effp->ininfo.rate
, p->b0, p->b1, p->b2, p->a1, p->a2
);
@@ -64,9 +90,8 @@
}
-
-int st_biquad_flow(eff_t effp, const st_sample_t *ibuf, st_sample_t *obuf,
- st_size_t *isamp, st_size_t *osamp)
+int st_biquad_flow(eff_t effp, const st_sample_t *ibuf,
+ st_sample_t *obuf, st_size_t *isamp, st_size_t *osamp)
{
biquad_t p = (biquad_t) effp->priv;
st_size_t len = (*isamp > *osamp)? *osamp : *isamp;
--- a/src/biquad.h
+++ b/src/biquad.h
@@ -1,48 +1,96 @@
/*
- * 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 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.
+ * 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.
+ * 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.
*/
-/* Biquad filter common functions (c) 2006 robs@users.sourceforge.net */
+/* Biquad filter common definitions (c) 2006-7 robs@users.sourceforge.net */
#ifndef biquad_included
#define biquad_included
-#include <math.h>
#include "st_i.h"
+
+typedef enum {
+ filter_LPF,
+ filter_HPF,
+ filter_BPF_CSG,
+ filter_BPF,
+ filter_notch,
+ filter_APF,
+ filter_peakingEQ,
+ filter_lowShelf,
+ filter_highShelf,
+ filter_LPF_1,
+ filter_HPF_1,
+ filter_BPF_SPK,
+ filter_BPF_SPK_N,
+ filter_AP1,
+ filter_AP2
+} filter_t;
+
+
+typedef enum {
+ width_bw_Hz,
+ /* The old, non-RBJ, non-freq-warped band-pass/reject response;
+ * leaving here for now just in case anybody misses it: */
+ width_bw_old,
+ width_bw_oct,
+ width_Q,
+ width_slope
+} width_t;
+
+
/* Private data for the biquad filter effects */
typedef struct biquad
{
- double gain;
+ double gain; /* For EQ filters */
double fc; /* Centre/corner/cutoff frequency */
- union {double q, bandwidth, slope;} width; /* Depending on filter type */
- st_bool dcNormalise; /* A treble filter should normalise at DC */
+ double width; /* Filter width; interpreted as per width_type */
+ width_t width_type;
+ filter_t filter_type;
+
double b2, b1, b0; /* Filter coefficients */
double a2, a1, a0; /* Filter coefficients */
st_sample_t i1, i2; /* Filter memory */
- double o1, o2; /* Filter memory */
+ double o1, o2; /* Filter memory */
} * biquad_t;
-int st_biquad_start(eff_t effp, char const * width_name);
-int st_biquad_flow(eff_t effp, const st_sample_t *ibuf, st_sample_t *obuf,
- st_size_t *isamp, st_size_t *osamp);
assert_static(sizeof(struct biquad) <= ST_MAX_EFFECT_PRIVSIZE,
/* else */ biquad_PRIVSIZE_too_big);
+
+int st_biquad_getopts(eff_t effp, int n, char **argv,
+ int min_args, int max_args, int fc_pos, int width_pos, int gain_pos,
+ char const * allowed_width_types, filter_t filter_type);
+int st_biquad_start(eff_t effp);
+int st_biquad_flow(eff_t effp, const st_sample_t *ibuf, st_sample_t *obuf,
+ st_size_t *isamp, st_size_t *osamp);
+
+#define BIQUAD_EFFECT(name,group,usage) \
+st_effect_t const * st_##name##_effect_fn(void) { \
+ static st_effect_t driver = { \
+ #name, "Usage: " #name " " usage, 0, \
+ group##_getopts, start, st_biquad_flow, \
+ st_effect_nothing_drain, st_effect_nothing, st_effect_nothing \
+ }; \
+ return &driver; \
+}
+
+#undef st_fail
+#define st_fail st_message_filename=effp->name,st_fail
#endif
--- a/src/biquadlh.c
+++ /dev/null
@@ -1,180 +1,0 @@
-/*
- * 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.
- */
-
-/* Sound Tools Effects: 2-pole variable Q & 1-pole low/high-pass filters.
- *
- * This implementation (c) 2007 robs@users.sourceforge.net
- *
- * 2-pole with default Q gives a Butterworth response.
- *
- * 2-pole filter design by Robert Bristow-Johnson <rbj@audioimagination.com>
- * see http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt
- *
- * 1-pole low-pass based on lowp.c:
- * (c) 2000 Chris Bagwell <cbagwell@sprynet.com>
- *
- * Algorithm: Recursive single pole lowpass filter
- *
- * Reference: The Scientist and Engineer's Guide to Digital Signal Processing
- *
- * output[N] = input[N] * A + output[N-1] * B
- *
- * X = exp(-2.0 * pi * Fc)
- * A = 1 - X
- * B = X
- * Fc = cutoff freq / sample rate
- *
- * Mimics an RC low-pass filter:
- *
- * ---/\/\/\/\----------->
- * |
- * --- C
- * ---
- * |
- * |
- * V
- *
- * 1-pole high-pass based on highp.c:
- * (c) 2000 Chris Bagwell <cbagwell@sprynet.com>
- *
- * Algorithm: Recursive single pole high-pass filter
- *
- * Reference: The Scientist and Engineer's Guide to Digital Processing
- *
- * output[N] = A0 * input[N] + A1 * input[N-1] + B1 * output[N-1]
- *
- * X = exp(-2.0 * pi * Fc)
- * A0 = (1 + X) / 2
- * A1 = -(1 + X) / 2
- * B1 = X
- * Fc = cutoff freq / sample rate
- *
- * Mimics an RC high-pass filter:
- *
- * || C
- * ----||--------->
- * || |
- * <
- * > R
- * <
- * |
- * V
- */
-
-#include "biquad.h"
-#include <string.h>
-#undef st_fail
-#define st_fail st_message_filename=effp->name,st_fail
-
-static int getopts(eff_t effp, int n, char **argv)
-{
- biquad_t p = (biquad_t) effp->priv;
- char dummy;
- char order;
-
- if (n != 0 && sscanf(argv[0], "-%1[12] %c", &order, &dummy) == 1)
- ++argv, --n;
- else order = strlen(effp->name) <= 5 /* lowp|highp */ ? '1' : '2';
- order -= '0';
-
- p->width.q = order == 2? sqrt(0.5) : 0;
- if (n < 1 || n > order || sscanf(argv[0], "%lf %c", &p->fc, &dummy) != 1 ||
- p->fc <= 0 || (n == 2 && (sscanf(argv[1], "%lf %c", &p->width.q, &dummy)
- != 1 || p->width.q <= 0))) {
- st_fail(effp->h->usage);
- return ST_EOF;
- }
- return ST_SUCCESS;
-}
-
-static int start(eff_t effp)
-{
- biquad_t p = (biquad_t) effp->priv;
- double w0 = 2 * M_PI * p->fc / effp->ininfo.rate;
-
- if (w0 > M_PI) {
- st_fail("cut-off frequency must be less than half the sample-rate (Nyquist rate)");
- return ST_EOF;
- }
- if (p->width.q) { /* 2-pole */
- double alpha = sin(w0)/(2*p->width.q);
-
- if (*effp->name == 'l') { /* lowpass */
- p->b0 = (1 - cos(w0))/2;
- p->b1 = 1 - cos(w0);
- p->b2 = (1 - cos(w0))/2;
- } else {
- p->b0 = (1 + cos(w0))/2;
- p->b1 = -(1 + cos(w0));
- p->b2 = (1 + cos(w0))/2;
- }
- p->a0 = 1 + alpha;
- p->a1 = -2*cos(w0);
- p->a2 = 1 - alpha;
- } else { /* 1-pole */
- p->a0 = 1;
- p->a1 = -exp(-w0);
- if (*effp->name == 'l') { /* lowpass */
- p->b0 = 1 + p->a1;
- p->b1 = 0;
- } else {
- p->b0 = (1 - p->a1)/2;
- p->b1 = -p->b0;
- }
- p->a2 = p->b2 = 0;
- }
- return st_biquad_start(effp, "Q");
-}
-
-st_effect_t const * st_lowp_effect_fn(void)
-{
- static st_effect_t driver = {
- "lowp", "Usage: lowp cutoff", 0,
- getopts, start, st_biquad_flow,
- st_effect_nothing_drain, st_effect_nothing, st_effect_nothing
- };
- return &driver;
-}
-
-st_effect_t const * st_highp_effect_fn(void)
-{
- static st_effect_t driver = {
- "highp", "Usage: highp cutoff", 0,
- getopts, start, st_biquad_flow,
- st_effect_nothing_drain, st_effect_nothing, st_effect_nothing
- };
- return &driver;
-}
-
-st_effect_t const * st_lowpass_effect_fn(void)
-{
- static st_effect_t driver = {
- "lowpass", "Usage: lowpass [-1|-2] frequency [Q]", 0,
- getopts, start, st_biquad_flow,
- st_effect_nothing_drain, st_effect_nothing, st_effect_nothing
- };
- return &driver;
-}
-
-st_effect_t const * st_highpass_effect_fn(void)
-{
- static st_effect_t driver = {
- "highpass", "Usage: highpass [-1|-2] frequency [Q]", 0,
- getopts, start, st_biquad_flow,
- st_effect_nothing_drain, st_effect_nothing, st_effect_nothing
- };
- return &driver;
-}
--- /dev/null
+++ b/src/biquads.c
@@ -1,0 +1,302 @@
+/*
+ * 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.
+ */
+
+/* Biquad filter effects (c) 2006-7 robs@users.sourceforge.net
+ *
+ * 2-pole filters designed by Robert Bristow-Johnson <rbj@audioimagination.com>
+ * see http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt
+ *
+ * 1-pole filters based on code (c) 2000 Chris Bagwell <cbagwell@sprynet.com>
+ * Algorithms: Recursive single pole low/high pass filter
+ * Reference: The Scientist and Engineer's Guide to Digital Signal Processing
+ *
+ * low-pass: output[N] = input[N] * A + output[N-1] * B
+ * X = exp(-2.0 * pi * Fc)
+ * A = 1 - X
+ * B = X
+ * Fc = cutoff freq / sample rate
+ *
+ * Mimics an RC low-pass filter:
+ *
+ * ---/\/\/\/\----------->
+ * |
+ * --- C
+ * ---
+ * |
+ * |
+ * V
+ *
+ * high-pass: output[N] = A0 * input[N] + A1 * input[N-1] + B1 * output[N-1]
+ * X = exp(-2.0 * pi * Fc)
+ * A0 = (1 + X) / 2
+ * A1 = -(1 + X) / 2
+ * B1 = X
+ * Fc = cutoff freq / sample rate
+ *
+ * Mimics an RC high-pass filter:
+ *
+ * || C
+ * ----||--------->
+ * || |
+ * <
+ * > R
+ * <
+ * |
+ * V
+ */
+
+
+#include "biquad.h"
+#include <string.h>
+#include <math.h>
+
+
+static int hilo1_getopts(eff_t effp, int n, char **argv) {
+ return st_biquad_getopts(effp, n, argv, 1, 1, 0, 1, 2, "",
+ *effp->name == 'l'? filter_LPF_1 : filter_HPF_1);
+}
+
+
+static int hilo2_getopts(eff_t effp, int n, char **argv) {
+ biquad_t p = (biquad_t) effp->priv;
+ if (n != 0 && strcmp(argv[0], "-1") == 0)
+ return hilo1_getopts(effp, n - 1, argv + 1);
+ if (n != 0 && strcmp(argv[0], "-2") == 0)
+ ++argv, --n;
+ p->width = sqrt(0.5); /* Default to Butterworth */
+ return st_biquad_getopts(effp, n, argv, 1, 2, 0, 1, 2, "qoh",
+ *effp->name == 'l'? filter_LPF : filter_HPF);
+}
+
+
+static int bandpass_getopts(eff_t effp, int n, char **argv) {
+ filter_t type = filter_BPF;
+ if (n != 0 && strcmp(argv[0], "-c") == 0)
+ ++argv, --n, type = filter_BPF_CSG;
+ return st_biquad_getopts(effp, n, argv, 2, 2, 0, 1, 2, "hqob", type);
+}
+
+
+static int bandrej_getopts(eff_t effp, int n, char **argv) {
+ return st_biquad_getopts(effp, n, argv, 2, 2, 0, 1, 2, "hqob", filter_notch);
+}
+
+
+static int allpass_getopts(eff_t effp, int n, char **argv) {
+ filter_t type = filter_APF;
+ int m;
+ if (n != 0 && strcmp(argv[0], "-1") == 0)
+ ++argv, --n, type = filter_AP1;
+ else if (n != 0 && strcmp(argv[0], "-2") == 0)
+ ++argv, --n, type = filter_AP2;
+ m = 1 + (type == filter_APF);
+ return st_biquad_getopts(effp, n, argv, m, m, 0, 1, 2, "hqo", type);
+}
+
+
+static int tone_getopts(eff_t effp, int n, char **argv) {
+ biquad_t p = (biquad_t) effp->priv;
+ p->width = 0.5;
+ p->fc = *effp->name == 'b'? 100 : 3000;
+ return st_biquad_getopts(effp, n, argv, 1, 3, 1, 2, 0, "shqo",
+ *effp->name == 'b'? filter_lowShelf: filter_highShelf);
+}
+
+
+static int equalizer_getopts(eff_t effp, int n, char **argv) {
+ return st_biquad_getopts(effp, n, argv, 3, 3, 0, 1, 2, "qoh", filter_peakingEQ);
+}
+
+
+static int band_getopts(eff_t effp, int n, char **argv) {
+ filter_t type = filter_BPF_SPK;
+ if (n != 0 && strcmp(argv[0], "-n") == 0)
+ ++argv, --n, type = filter_BPF_SPK_N;
+ return st_biquad_getopts(effp, n, argv, 1, 2, 0, 1, 2, "hqo", type);
+}
+
+
+static int start(eff_t effp)
+{
+ biquad_t p = (biquad_t) effp->priv;
+ double w0 = 2 * M_PI * p->fc / effp->ininfo.rate;
+ double A = exp(p->gain / 40 * log(10));
+ double alpha = 0;
+
+ if (w0 > M_PI) {
+ st_fail("frequency must be less than half the sample-rate (Nyquist rate)");
+ return ST_EOF;
+ }
+
+ /* Set defaults: */
+ p->b0 = p->b1 = p->b2 = p->a1 = p->a2 = 0;
+ p->a0 = 1;
+
+ if (p->width) switch (p->width_type) {
+ case width_slope:
+ alpha = sin(w0)/2 * sqrt((A + 1/A)*(1/p->width - 1) + 2);
+ break;
+
+ case width_Q:
+ alpha = sin(w0)/(2*p->width);
+ break;
+
+ case width_bw_oct:
+ alpha = sin(w0)*sinh(log(2)/2 * p->width * w0/sin(w0));
+ break;
+
+ case width_bw_Hz:
+ alpha = sin(w0)/(2*p->fc/p->width);
+ break;
+
+ case width_bw_old:
+ alpha = tan(M_PI * p->width / effp->ininfo.rate);
+ break;
+ }
+ switch (p->filter_type) {
+ case filter_LPF: /* H(s) = 1 / (s^2 + s/Q + 1) */
+ p->b0 = (1 - cos(w0))/2;
+ p->b1 = 1 - cos(w0);
+ p->b2 = (1 - cos(w0))/2;
+ p->a0 = 1 + alpha;
+ p->a1 = -2*cos(w0);
+ p->a2 = 1 - alpha;
+ break;
+
+ case filter_HPF: /* H(s) = s^2 / (s^2 + s/Q + 1) */
+ p->b0 = (1 + cos(w0))/2;
+ p->b1 = -(1 + cos(w0));
+ p->b2 = (1 + cos(w0))/2;
+ p->a0 = 1 + alpha;
+ p->a1 = -2*cos(w0);
+ p->a2 = 1 - alpha;
+ break;
+
+ case filter_BPF_CSG: /* H(s) = s / (s^2 + s/Q + 1) (constant skirt gain, peak gain = Q) */
+ p->b0 = sin(w0)/2;
+ p->b1 = 0;
+ p->b2 = -sin(w0)/2;
+ p->a0 = 1 + alpha;
+ p->a1 = -2*cos(w0);
+ p->a2 = 1 - alpha;
+ break;
+
+ case filter_BPF: /* H(s) = (s/Q) / (s^2 + s/Q + 1) (constant 0 dB peak gain) */
+ p->b0 = alpha;
+ p->b1 = 0;
+ p->b2 = -alpha;
+ p->a0 = 1 + alpha;
+ p->a1 = -2*cos(w0);
+ p->a2 = 1 - alpha;
+ break;
+
+ case filter_notch: /* H(s) = (s^2 + 1) / (s^2 + s/Q + 1) */
+ p->b0 = 1;
+ p->b1 = -2*cos(w0);
+ p->b2 = 1;
+ p->a0 = 1 + alpha;
+ p->a1 = -2*cos(w0);
+ p->a2 = 1 - alpha;
+ break;
+
+ case filter_APF: /* H(s) = (s^2 - s/Q + 1) / (s^2 + s/Q + 1) */
+ p->b0 = 1 - alpha;
+ p->b1 = -2*cos(w0);
+ p->b2 = 1 + alpha;
+ p->a0 = 1 + alpha;
+ p->a1 = -2*cos(w0);
+ p->a2 = 1 - alpha;
+ break;
+
+ case filter_peakingEQ: /* H(s) = (s^2 + s*(A/Q) + 1) / (s^2 + s/(A*Q) + 1) */
+ p->b0 = 1 + alpha*A;
+ p->b1 = -2*cos(w0);
+ p->b2 = 1 - alpha*A;
+ p->a0 = 1 + alpha/A;
+ p->a1 = -2*cos(w0);
+ p->a2 = 1 - alpha/A;
+ break;
+
+ case filter_lowShelf: /* H(s) = A * (s^2 + (sqrt(A)/Q)*s + A)/(A*s^2 + (sqrt(A)/Q)*s + 1) */
+ p->b0 = A*( (A+1) - (A-1)*cos(w0) + 2*sqrt(A)*alpha );
+ p->b1 = 2*A*( (A-1) - (A+1)*cos(w0) );
+ p->b2 = A*( (A+1) - (A-1)*cos(w0) - 2*sqrt(A)*alpha );
+ p->a0 = (A+1) + (A-1)*cos(w0) + 2*sqrt(A)*alpha;
+ p->a1 = -2*( (A-1) + (A+1)*cos(w0) );
+ p->a2 = (A+1) + (A-1)*cos(w0) - 2*sqrt(A)*alpha;
+ break;
+
+ case filter_highShelf: /* H(s) = A * (A*s^2 + (sqrt(A)/Q)*s + 1)/(s^2 + (sqrt(A)/Q)*s + A) */
+ p->b0 = A*( (A+1) + (A-1)*cos(w0) + 2*sqrt(A)*alpha );
+ p->b1 = -2*A*( (A-1) + (A+1)*cos(w0) );
+ p->b2 = A*( (A+1) + (A-1)*cos(w0) - 2*sqrt(A)*alpha );
+ p->a0 = (A+1) - (A-1)*cos(w0) + 2*sqrt(A)*alpha;
+ p->a1 = 2*( (A-1) - (A+1)*cos(w0) );
+ p->a2 = (A+1) - (A-1)*cos(w0) - 2*sqrt(A)*alpha;
+ break;
+
+ case filter_LPF_1: /* single-pole */
+ p->a1 = -exp(-w0);
+ p->b0 = 1 + p->a1;
+ break;
+
+ case filter_HPF_1: /* single-pole */
+ p->a1 = -exp(-w0);
+ p->b0 = (1 - p->a1)/2;
+ p->b1 = -p->b0;
+ break;
+
+ case filter_BPF_SPK: case filter_BPF_SPK_N: {
+ double bw_Hz;
+ if (!p->width)
+ p->width = p->fc / 2;
+ bw_Hz = p->width_type == width_Q? p->fc / p->width :
+ p->width_type == width_bw_Hz? p->width :
+ p->fc * (pow(2, p->width) - 1) * pow(2, -0.5 * p->width); /* bw_oct */
+ #include "band.h" /* Has different licence */
+ break;
+ }
+
+ case filter_AP1: /* Experimental 1-pole all-pass from Tom Erbe @ UCSD */
+ p->b0 = exp(-w0);
+ p->b1 = -1;
+ p->a1 = -exp(-w0);
+ break;
+
+ case filter_AP2: /* Experimental 2-pole all-pass from Tom Erbe @ UCSD */
+ p->b0 = 1 - sin(w0);
+ p->b1 = -2 * cos(w0);
+ p->b2 = 1 + sin(w0);
+ p->a0 = 1 + sin(w0);
+ p->a1 = -2 * cos(w0);
+ p->a2 = 1 - sin(w0);
+ break;
+ }
+ return st_biquad_start(effp);
+}
+
+
+BIQUAD_EFFECT(highp, hilo1, "cutoff-frequency")
+BIQUAD_EFFECT(lowp, hilo1, "cutoff-frequency")
+BIQUAD_EFFECT(highpass, hilo2, "[-1|-2] frequency [width[q|o|h]]")
+BIQUAD_EFFECT(lowpass, hilo2, "[-1|-2] frequency [width[q|o|h]]")
+BIQUAD_EFFECT(bandpass, bandpass, "[-c] frequency width[h|q|o]")
+BIQUAD_EFFECT(bandreject,bandrej, "frequency width[h|q|o]")
+BIQUAD_EFFECT(allpass, allpass, "frequency width[h|q|o]")
+BIQUAD_EFFECT(bass, tone, "gain [frequency [width[s|h|q|o]]]")
+BIQUAD_EFFECT(treble, tone, "gain [frequency [width[s|h|q|o]]]")
+BIQUAD_EFFECT(equalizer, equalizer,"frequency width[q|o|h] gain")
+BIQUAD_EFFECT(band, band, "[-n] center [width[h|q|o]]")
--- a/src/breject.c
+++ /dev/null
@@ -1,98 +1,0 @@
-/*
-
- Band-reject effect file for SoX
- Copyright (C) 1999 Jan Paul Schmidt <jps@fundament.org>
-
- 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, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
- Code based on the band-reject implementation in
-
- Sound Processing Kit - A C++ Class Library for Audio Signal Processing
- Copyright (C) 1995-1998 Kai Lassfolk
-
- as described in
-
- Computer music: synthesis, composition, and performance
- Charles Dodge, Thomas A. Jerse
- [2nd ed.]
- Page 217
-
- */
-
-#include <math.h>
-
-#include "st_i.h"
-#include "btrworth.h"
-
-static st_effect_t st_bandreject_effect;
-
-static int st_bandreject_getopts(eff_t effp, int n, char **argv)
-{
- butterworth_t butterworth = (butterworth_t)effp->priv;
-
- if (n != 2) {
- st_fail(st_bandreject_effect.usage);
- return (ST_EOF);
- }
-
- st_butterworth_start (effp);
-
- if (!(sscanf (argv [0], "%lf", &butterworth->frequency))) {
- st_fail("bandreject: illegal frequency");
- return (ST_EOF);
- }
-
- if (!(sscanf (argv [1], "%lf", &butterworth->bandwidth))) {
- st_fail("bandreject: illegal bandwidth");
- return (ST_EOF);
- }
- return (ST_SUCCESS);
-}
-
-static int st_bandreject_start(eff_t effp)
-{
- butterworth_t butterworth = (butterworth_t) effp->priv;
- double c;
- double d;
-
- c = tan (M_PI * butterworth->bandwidth / effp->ininfo.rate);
- d = 2 * cos (2 * M_PI * butterworth->frequency / effp->ininfo.rate);
-
- butterworth->a [0] = 1.0 / (1.0 + c);
- butterworth->a [1] = -d * butterworth->a[0];
- butterworth->a [2] = butterworth->a[0];
-
- butterworth->b [0] = butterworth->a[1];
- butterworth->b [1] = (1.0 - c) * butterworth->a[0];
- st_butterworth_plot(effp);
- return (ST_SUCCESS);
-}
-
-static st_effect_t st_bandreject_effect = {
- "bandreject",
- "Usage: bandreject FREQUENCY BANDWIDTH",
- 0,
- st_bandreject_getopts,
- st_bandreject_start,
- st_butterworth_flow,
- st_effect_nothing_drain,
- st_effect_nothing,
- st_effect_nothing
-};
-
-const st_effect_t *st_bandreject_effect_fn(void)
-{
- return &st_bandreject_effect;
-}
--- a/src/btrworth.c
+++ /dev/null
@@ -1,113 +1,0 @@
-/*
- Butterworth effect file for SoX
- Copyright (C) 1999 Jan Paul Schmidt <jps@fundament.org>
-
- This source code 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 source code 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
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
- 02111-1307 USA
-
- Code based on the butterworth implementation in
-
- Sound Processing Kit - A C++ Class Library for Audio Signal Processing
- Copyright (C) 1995-1998 Kai Lassfolk
-
- as described in
-
- Computer music: synthesis, composition, and performance
- Charles Dodge, Thomas A. Jerse
- [2nd ed.]
- Page 214
- */
-
-#include <math.h>
-
-#include "st_i.h"
-#include "btrworth.h"
-
-int st_butterworth_start (eff_t effp)
-{
- butterworth_t butterworth = (butterworth_t) effp->priv;
-
- butterworth->x [0] = 0.0;
- butterworth->x [1] = 0.0;
- butterworth->y [0] = 0.0;
- butterworth->y [1] = 0.0;
- return (ST_SUCCESS);
-}
-
-void st_butterworth_plot (eff_t effp)
-{
- butterworth_t butterworth = (butterworth_t) effp->priv;
-
- if (effp->globalinfo->octave_plot_effect)
- {
- printf(
- "title('SoX effect: %s centre=%g width=%g (rate=%u)')\n"
- "xlabel('Frequency (Hz)')\n"
- "ylabel('Amplitude Response (dB)')\n"
- "Fs=%u;minF=10;maxF=Fs/2;\n"
- "axis([minF maxF -95 5])\n"
- "sweepF=logspace(log10(minF),log10(maxF),200);\n"
- "grid on\n"
- "[h,w]=freqz([%f %f %f],[1 %f %f],sweepF,Fs);\n"
- "semilogx(w,20*log10(h),'b')\n"
- "pause\n"
- , effp->name, butterworth->frequency, butterworth->bandwidth
- , effp->ininfo.rate, effp->ininfo.rate
- , butterworth->a[0]
- , butterworth->a[1]
- , butterworth->a[2]
- , butterworth->b[0]
- , butterworth->b[1]
- );
- }
-}
-
-int st_butterworth_flow (eff_t effp, const st_sample_t *ibuf, st_sample_t *obuf,
- st_size_t *isamp, st_size_t *osamp)
-{
- butterworth_t butterworth = (butterworth_t) effp->priv;
-
- double in;
- double out;
-
- int len;
- int done;
-
- len = ((*isamp > *osamp) ? *osamp : *isamp);
-
- for (done = 0; done < len; done++) {
- in = *ibuf++;
-
- out =
- butterworth->a [0] * in +
- butterworth->a [1] * butterworth->x [0] +
- butterworth->a [2] * butterworth->x [1] -
- butterworth->b [0] * butterworth->y [0] -
- butterworth->b [1] * butterworth->y [1];
-
- butterworth->x [1] = butterworth->x [0];
- butterworth->x [0] = in;
- butterworth->y [1] = butterworth->y [0];
- butterworth->y [0] = out;
-
- ST_SAMPLE_CLIP_COUNT(out, effp->clips);
-
- *obuf++ = out;
- }
-
- *isamp = len;
- *osamp = len;
- return (ST_SUCCESS);
-}
--- a/src/btrworth.h
+++ /dev/null
@@ -1,53 +1,0 @@
-/*
-
- Butterworth effect file for SoX
- Copyright (C) 1999 Jan Paul Schmidt <jps@fundament.org>
-
- 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, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
- Code based on the butterworth implementation in
-
- Sound Processing Kit - A C++ Class Library for Audio Signal Processing
- Copyright (C) 1995-1998 Kai Lassfolk
-
- as described in
-
- Computer music: synthesis, composition, and performance
- Charles Dodge, Thomas A. Jerse
- [2nd ed.]
- Page 214
-
- */
-
-int st_butterworth_start (eff_t effp);
-void st_butterworth_plot (eff_t effp);
-int st_butterworth_flow (eff_t effp, const st_sample_t *ibuf, st_sample_t *obuf,
- st_size_t *isamp, st_size_t *osamp);
-
-typedef struct butterworth {
- double x [2];
- double y [2];
-
- double a [3];
- double b [2];
-
- /*
- * Cut off frequency for low-pass and high-pass,
- * center frequency for band-pass
- */
- double frequency;
-
- double bandwidth;
-} *butterworth_t;
--- a/src/equalizer.c
+++ /dev/null
@@ -1,114 +1,0 @@
-/*
- Equalizer filter effect file for SoX
- Copyright (C) 2006 Pascal Giard <evilynux@gmail.com>
-
- 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, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
- Code based on the biquad filters described in
-
- Cookbook formulae for audio EQ biquad filter coefficients
- by Robert Bristow-Johnson <rbj@audioimagination.com>
-
- Theory:
- y[n] = (a0/b0)*x[n] + (b1/a0)*x[n-1] + (b2/a0)*x[n-2]
- - (a1/a0)*y[n-1] - (a2/a0)*y[n-2]
-
- Where:
- w0 = 2*M_PI*cfreq/srate
- A = 10^(gain/40)
- alpha = sin(w0)/( 2*Q )
-
- For a PeakingEQ filter:
- b0 = 1 + alpha*A
- b1 = -2*cos(w0)
- b2 = 1 - alpha*A
- a0 = 1 + alpha/A
- a1 = -2*cos(w0)
- a2 = 1 - alpha/A
-
- Reminder:
- Q = sqrt(2^n)/(2^n - 1) where n is bandwidth in octave
- n = log2(bw) where bw is bandwidth in Hz
-
- Transfer function is:
- (a0/b0) + (b1/a0)z^-1 + (b2/a0)z^-2
- H(z) = -----------------------------------
- 1 + (a1/a0)z^-1 + (a2/a0)z^-2
- */
-
-#include "biquad.h"
-
-static int equalizer_getopts(eff_t effp, int n, char **argv)
-{
- biquad_t eq = (biquad_t) effp->priv;
- char dummy;
-
- if (n != 3 ||
- sscanf(argv[0], "%lf %c", &eq->fc , &dummy) != 1 ||
- sscanf(argv[1], "%lf %c", &eq->width.q, &dummy) != 1 ||
- sscanf(argv[2], "%lf %c", &eq->gain , &dummy) != 1 ||
- eq->fc <= 0 ||
- eq->width.q <= 0) {
- st_fail(effp->h->usage);
- return ST_EOF;
- }
- return ST_SUCCESS;
-}
-
-static int equalizer_start(eff_t effp)
-{
- biquad_t eq = (biquad_t) effp->priv;
- double w0;
- double amp;
- double alpha;
-
- /* Set the filter constants */
- w0 = 2*M_PI*eq->fc/effp->ininfo.rate;
- amp = pow( 10, eq->gain/40 );
- alpha = sin(w0)/( 2*eq->width.q );
-
- st_debug("cfreq: %fHz", eq->fc);
- st_debug("Q: %f", eq->width.q);
- st_debug("gain: %fdB", eq->gain);
- st_debug("w0: %f", w0);
- st_debug("amp: %f", amp);
- st_debug("alpha: %f", alpha);
-
- /* Initialisation */
- eq->b0 = 1 + alpha*amp;
- eq->b1 = -2*cos(w0);
- eq->b2 = 1 - alpha*amp;
- eq->a0 = 1 + alpha/amp;
- eq->a1 = -2*cos(w0);
- eq->a2 = 1 - alpha/amp;
-
- return st_biquad_start(effp, "Q");
-}
-
-const st_effect_t *st_equalizer_effect_fn(void)
-{
- static st_effect_t driver = {
- "equalizer",
- "Usage: equalizer central-frequency Q gain",
- 0,
- equalizer_getopts,
- equalizer_start,
- st_biquad_flow,
- st_effect_nothing_drain,
- st_effect_nothing,
- st_effect_nothing
- };
- return &driver;
-}
--- a/src/handlers.c
+++ b/src/handlers.c
@@ -8,7 +8,6 @@
*/
#include "st_i.h"
-#include "btrworth.h"
/*
* Sound Tools file format and effect tables.
@@ -90,6 +89,7 @@
*/
st_effect_fn_t st_effect_fns[] = {
+ st_allpass_effect_fn,
st_avg_effect_fn,
st_band_effect_fn,
st_bandpass_effect_fn,
--- a/src/st_i.h
+++ b/src/st_i.h
@@ -315,6 +315,7 @@
extern st_effect_fn_t st_effect_fns[];
+extern const st_effect_t *st_allpass_effect_fn(void);
extern const st_effect_t *st_avg_effect_fn(void);
extern const st_effect_t *st_band_effect_fn(void);
extern const st_effect_t *st_bandpass_effect_fn(void);
--- a/src/tone.c
+++ /dev/null
@@ -1,152 +1,0 @@
-/*
- * 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.
- */
-
-/* Sound Tools Tone Control Effects: bass & treble
- *
- * The bass and treble effects approximate to the venerable Baxandall
- * circuit used in the analogue world.
- *
- * Filter design by Robert Bristow-Johnson <rbj@audioimagination.com>
- * see http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt
- *
- * This implementation (c) 2006 robs@users.sourceforge.net
- */
-
-#include "biquad.h"
-
-
-
-static int getopts(eff_t effp, int n, char **argv, double fc, int dcNormalise)
-{
- st_bool isFcSet = st_false;
- double opt1 = HUGE_VAL, opt2 = HUGE_VAL;
- biquad_t p = (biquad_t) effp->priv;
- int ret = ST_SUCCESS;
- char dummy;
-
- /* Initialise non-zero numbers: */
- p->dcNormalise = dcNormalise;
- p->fc = fc;
- p->width.slope = 0.5;
-
- /*
- * This block is a little complicated -- all because I wanted
- * to make it easy for the user i.e. order doesn't matter for
- * optional arguments.
- * This is made possible because slope <= 1 and by
- * insisting that the centre-frequency is > 1 (Hz).
- */
- if (n > 0 &&
- sscanf(argv[0], "%lf %c", &p->gain, &dummy) == 1 &&
- (n < 2 || sscanf(argv[1], "%lf %c", &opt1, &dummy) == 1) &&
- (n < 3 || sscanf(argv[2], "%lf %c", &opt2, &dummy) == 1) &&
- (n < 4)) {
- if (opt1 != HUGE_VAL) {
- if (opt1 <= 0)
- ret = ST_EOF;
- else {
- if (opt1 > 1) {
- p->fc = opt1;
- isFcSet = st_true;
- } else
- p->width.slope = opt1;
- if (opt2 != HUGE_VAL) {
- if (opt2 > 1) {
- if (isFcSet)
- ret = ST_EOF;
- else
- p->fc = opt2;
- } else if (opt2 > 0) {
- if (!isFcSet)
- ret = ST_EOF;
- else
- p->width.slope = opt2;
- } else
- ret = ST_EOF;
- }
- }
- }
- if (dcNormalise)
- p->gain = -p->gain;
- }
-
- if (ret == ST_EOF)
- st_fail(effp->h->usage);
- return ret;
-}
-
-
-
-static int bass_getopts (eff_t e,int n,char **a){return getopts(e,n,a, 100,0);}
-static int treble_getopts(eff_t e,int n,char **a){return getopts(e,n,a,3000,1);}
-
-
-
-static int st_biquad_shelf_start(eff_t effp)
-{
- biquad_t p = (biquad_t) effp->priv;
-
- /* Calculate intermediates: */
- double A = exp(p->gain / 40 * log(10));
- double w0 = 2 * M_PI * p->fc / effp->ininfo.rate;
- double alpha = sin(w0)/2 * sqrt( (A + 1/A)*(1/p->width.slope - 1) + 2 );
-
- /* Calculate filter coefficients: */
- p->b0 = A*( (A+1) - (A-1)*cos(w0) + 2*sqrt(A)*alpha );
- p->b1 = 2*A*( (A-1) - (A+1)*cos(w0) );
- p->b2 = A*( (A+1) - (A-1)*cos(w0) - 2*sqrt(A)*alpha );
- p->a0 = (A+1) + (A-1)*cos(w0) + 2*sqrt(A)*alpha;
- p->a1 = -2*( (A-1) + (A+1)*cos(w0) );
- p->a2 = (A+1) + (A-1)*cos(w0) - 2*sqrt(A)*alpha;
-
- return st_biquad_start(effp, "slope");
-}
-
-
-
-st_effect_t const * st_bass_effect_fn(void)
-{
- static st_effect_t driver = {
- "bass",
- "Usage: bass gain [frequency] [slope]",
- 0,
- bass_getopts,
- st_biquad_shelf_start,
- st_biquad_flow,
- st_effect_nothing_drain,
- st_effect_nothing,
- st_effect_nothing
- };
- return &driver;
-}
-
-
-
-st_effect_t const * st_treble_effect_fn(void)
-{
- static st_effect_t driver = {
- "treble",
- "Usage: treble gain [frequency] [slope]",
- 0,
- treble_getopts,
- st_biquad_shelf_start,
- st_biquad_flow,
- st_effect_nothing_drain,
- st_effect_nothing,
- st_effect_nothing
- };
- return &driver;
-}