Butterworth

Butterworth

A collection of 2nd-order butterworth filter collections. These will be explained and probably coded up a little bit better later. For now, these are imports from Soundpipe.

The digital Butterworth filter is initially designed in the S-plane like you would an analogue filter, which results in a special transfer function. This transfer function then gets digitized to the Z-plane using a process known as the bilinear transform, or BLT. By the time this filter gets implemented in C code, this transformation has already been applied. What you're left with is a biquad with a bunch of constants from the BLT.

Going through the motions of converting the Butterworth filter from the S-plane to the Z-plane is a little bit beyond the scope of this sndkit. However, Julius Smith has a page that derives a Second-order butterworth lowpass filter.


<<butterworth.h>>=
#ifndef SK_BUTTERWORTH_H
#define SK_BUTLERWORTH_H

#ifndef SKFLT
#define SKFLT float
#endif

typedef struct sk_butterworth sk_butterworth;

void sk_butterworth_init(sk_butterworth *bw, int sr);
void sk_butterworth_freq(sk_butterworth *bw, SKFLT freq);
void sk_butterworth_bandwidth(sk_butterworth *but, SKFLT bw);

SKFLT sk_butlp_tick(sk_butterworth *bw, SKFLT in);
SKFLT sk_buthp_tick(sk_butterworth *bw, SKFLT in);
SKFLT sk_butbp_tick(sk_butterworth *bw, SKFLT in);

#ifdef SK_BUTTERWORTH_PRIV
struct sk_butterworth {
    SKFLT freq, lfreq;
    SKFLT bw, lbw; /* for bandpass filter only */
    SKFLT a[7];
    SKFLT pidsr, tpidsr;
};
#endif

#endif

This code is based off the soundpipe implementation, slightly reworked to make it so that malloc is not used.

Tangled Code

<<butterworth.c>>=
#include <math.h>
#include <stdint.h>
#include <stdlib.h>

#define SK_BUTTERWORTH_PRIV
#include "butterworth.h"

#define ROOT2 1.4142135623730950488

#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
<<common>>
<<filters>>

Common

The butterworth filters share some common code, defined below.

The filter function performs the difference equation to compute one sample of audio. in is the input signal, and a refers to the entire filter state: indices 0-4 hold filter coefficients, and 5-6 hold previously computed values. This has been reworked from the cannonical difference equation to be more optimized.


sk_butterworth_init initializes the filter data for all butterworth filters.

sk_butterworth_freq sets the frequency.

<<common>>=
static SKFLT filter(SKFLT in, SKFLT *a)
{
    SKFLT t, y;
    /* a5 = t(n - 1); a6 = t(n - 2) */
    t = in - a[3]*a[5] - a[4]*a[6];
    y = t*a[0] + a[1]*a[5] + a[2]*a[6];
    a[6] = a[5];
    a[5] = t;
    return y;
}

void sk_butterworth_init(sk_butterworth *bw, int sr)
{
    int i;
    sk_butterworth_freq(bw, 1000.0);
    sk_butterworth_bandwidth(bw, 1000.0);
    bw->lfreq = -1;
    bw->lbw = -1;
    bw->pidsr = M_PI / (SKFLT)sr;
    for (i = 0; i < 7; i++) bw->a[i] = 0.0;
}

void sk_butterworth_freq(sk_butterworth *bw, SKFLT freq)
{
    bw->freq = freq;
}

void sk_butterworth_bandwidth(sk_butterworth *but, SKFLT bw)
{
    but->bw = bw;
}

Filters

Lowpass

<<filters>>=
SKFLT sk_butlp_tick(sk_butterworth *bw, SKFLT in)
{
    if (bw->freq != bw->lfreq) {
        SKFLT *a, c;
        a = bw->a;
        bw->lfreq = bw->freq;
        /* derive C constant used in BLT */
        c = 1.0 / tan((SKFLT)(bw->pidsr * bw->lfreq));

        /* perform BLT, store components */
        a[0] = 1.0 / (1.0 + c*ROOT2 + c*c);
        a[1] = 2*a[0];
        a[2] = a[0];
        a[3] = 2.0 * (1.0 - c*c) * a[0];
        a[4] = (1.0 - c*ROOT2 + c*c) * a[0];
    }

    return filter(in, bw->a);
}

Highpass

<<filters>>=
SKFLT sk_buthp_tick(sk_butterworth *bw, SKFLT in)
{
    if (bw->freq != bw->lfreq) {
        SKFLT *a, c;
        a = bw->a;
        bw->lfreq = bw->freq;
        /* derive C constant used in BLT */
        c = tan((SKFLT)(bw->pidsr * bw->freq));

        /* perform BLT, store components */
        a[0] = 1.0 / (1.0 + c*ROOT2 + c*c);
        a[1] = -2*a[0];
        a[2] = a[0];
        a[3] = 2.0 * (c*c - 1.0) * a[0];
        a[4] = (1.0 - c*ROOT2 + c*c) * a[0];
    }

    return filter(in, bw->a);
}

Bandpass

<<filters>>=
SKFLT sk_butbp_tick(sk_butterworth *bw, SKFLT in)
{
    if (bw->bw != bw->lbw || bw->freq != bw->lfreq) {
        SKFLT *a, c, d;
        a = bw->a;
        bw->lfreq = bw->freq;
        bw->lbw = bw->bw;

        /* Perform BLT and store components */
        c = 1.0 / tan((SKFLT)(bw->pidsr * bw->bw));
        d = 2.0 * cos((SKFLT)(2.0*bw->pidsr * bw->freq));
        a[0] = 1.0 / (1.0 + c);
        a[1] = 0.0;
        a[2] = -a[0];
        a[3] = - c * d * a[0];
        a[4] = (c - 1.0) * a[0];
    }

    return filter(in, bw->a);
}