ref: 65f3eca4990d29f08256ef7b72f07561062f7682
parent: d14f798ae9d14cc6192f3c764976820a288bd8af
author: rrt <rrt>
date: Mon Nov 13 20:32:09 EST 2006
Add files missing from earlier commit
--- /dev/null
+++ b/src/biquad.c
@@ -1,0 +1,32 @@
+#include "biquad.h"
+
+
+
+int st_biquad_flow(eff_t effp, 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;
+ *isamp = *osamp = len;
+
+ while (len--)
+ {
+ double o0 = *ibuf*p->b0 +p->i1*p->b1 +p->i2*p->b2 -p->o1*p->a1 -p->o2*p->a2;
+ p->i2 = p->i1, p->i1 = *ibuf++;
+ p->o2 = p->o1, p->o1 = o0;
+ *obuf++ = ST_ROUND_CLIP_COUNT(o0, p->clippedCount);
+ }
+ return ST_SUCCESS;
+}
+
+
+
+int st_biquad_stop(eff_t effp) /* Stop processing, warn if overflows */
+{
+ biquad_t p = (biquad_t) effp->priv;
+ if (p->clippedCount != 0)
+ {
+ st_warn("%s: %d samples clipped", effp->name, p->clippedCount);
+ }
+ return ST_SUCCESS;
+}
--- /dev/null
+++ b/src/biquad.h
@@ -1,0 +1,32 @@
+#ifndef biquad_included
+#define biquad_included
+
+#include <math.h>
+#include "st_i.h"
+
+/* Private data for the biquad filter effects */
+typedef struct biquad
+{
+ double gain;
+ double fc; /* Centre/corner/cutoff frequency */
+ double oomph; /* Q, BW, or Slope */
+ bool dcNormalise; /* A treble filter should normalise at DC */
+
+ double b2, b1, b0; /* Filter coefficients */
+ double a2, a1; /* Filter coefficients; a0 = 1 */
+
+ st_sample_t i1, i2; /* Filter memory */
+ double o1, o2; /* Filter memory */
+
+ unsigned clippedCount;
+} * biquad_t;
+
+int st_biquad_flow(eff_t effp, st_sample_t *ibuf, st_sample_t *obuf,
+ st_size_t *isamp, st_size_t *osamp);
+
+int st_biquad_stop(eff_t effp);
+
+assert_static(sizeof(struct biquad) <= ST_MAX_EFFECT_PRIVSIZE,
+ /* else */ biquad_PRIVSIZE_too_big);
+
+#endif
--- /dev/null
+++ b/src/tone.c
@@ -1,0 +1,187 @@
+/*
+ * 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>
+ *
+ * This implementation (c) 2006 aquegg
+ *
+ * See LICENSE file for further copyright information.
+ */
+
+
+
+#include "biquad.h"
+
+#include <memory.h>
+
+
+
+static int getopts(eff_t effp, int n, char **argv, double fc, int dcNormalise)
+{
+ bool isFcSet = false;
+ double opt1 = HUGE_VAL, opt2 = HUGE_VAL;
+ biquad_t p = (biquad_t) effp->priv;
+
+ /* Zero all numbers, set all bools to false: */
+ memset(p, 0, sizeof(*p));
+
+ /* Initialise non-zero numbers: */
+ p->dcNormalise = dcNormalise;
+ p->fc = fc;
+ p->oomph = 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 (or oomph) <= 1 and by
+ * insisting that the centre-frequency is > 1 (Hz).
+ */
+ if (n > 0 &&
+ sscanf(argv[0], "%lf", &p->gain) &&
+ (n < 2 || sscanf(argv[1], "%lf", &opt1)) &&
+ (n < 3 || sscanf(argv[2], "%lf", &opt2)) &&
+ (n < 4))
+ do {
+ if (opt1 != HUGE_VAL)
+ {
+ if (opt1 > 1)
+ {
+ p->fc = opt1;
+ isFcSet = true;
+ }
+ else if (opt1 > 0)
+ {
+ p->oomph = opt1;
+ }
+ else break; /* error */
+ if (opt2 != HUGE_VAL)
+ {
+ if (opt2 > 1)
+ {
+ if (isFcSet) break; /* error */
+ p->fc = opt2;
+ }
+ else if (opt2 > 0)
+ {
+ if (!isFcSet) break; /* error */
+ p->oomph = opt2;
+ }
+ else break; /* error */
+ }
+ }
+ if (dcNormalise)
+ {
+ p->gain = -p->gain;
+ }
+ return ST_SUCCESS;
+ } while (0);
+
+ st_fail(effp->h->usage);
+ return ST_EOF;
+}
+
+
+
+static int st_bass_getopts (eff_t e, int n, char **a) {return getopts(e, n, a, 100, 0);}
+static int st_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->oomph - 1) + 2 );
+ double a0;
+
+ /* Calculate filter coefficients: */
+ p->b0 = A*( (A+1) - (A-1)*cos(w0) + 2*sqrt(A)*alpha ); /* Numerator. */
+ p->b1 = 2*A*( (A-1) - (A+1)*cos(w0) );
+ p->b2 = A*( (A+1) - (A-1)*cos(w0) - 2*sqrt(A)*alpha );
+ a0 = (A+1) + (A-1)*cos(w0) + 2*sqrt(A)*alpha; /* Denominator. */
+ p->a1 = -2*( (A-1) + (A+1)*cos(w0) );
+ p->a2 = (A+1) + (A-1)*cos(w0) - 2*sqrt(A)*alpha;
+
+ /* Simplify: */
+ p->b2 = p->b2/a0;
+ p->b1 = p->b1/a0;
+ p->b0 = p->b0/a0;
+ p->a2 = p->a2/a0;
+ p->a1 = p->a1/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)
+ {
+ printf(
+ "title('SoX effect: %s gain=%g centre=%g slope=%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"
+ "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, p->gain, p->fc, p->oomph
+ , effp->ininfo.rate, effp->ininfo.rate
+ , p->b0, p->b1, p->b2, p->a1, p->a2
+ );
+ exit(0);
+ }
+ return ST_SUCCESS;
+}
+
+
+
+static st_effect_t st_bass_effect = {
+ "bass",
+ "Usage: bass gain [frequency] [slope]",
+ 0,
+ st_bass_getopts,
+ st_biquad_shelf_start,
+ st_biquad_flow,
+ st_effect_nothing_drain,
+ st_biquad_stop
+};
+
+
+
+st_effect_t const * st_bass_effect_fn(void)
+{
+ return &st_bass_effect;
+}
+
+
+
+static st_effect_t st_treble_effect = {
+ "treble",
+ "Usage: treble gain [frequency] [slope]",
+ 0,
+ st_treble_getopts,
+ st_biquad_shelf_start,
+ st_biquad_flow,
+ st_effect_nothing_drain,
+ st_biquad_stop
+};
+
+
+
+st_effect_t const * st_treble_effect_fn(void)
+{
+ return &st_treble_effect;
+}