Smoother
Overview.
smoother
is a one-pole smoothing filter.
The smoothing filter, sometimes called a portamento filter is an indispensible tool for a sound designer. Mostly used to smooth out steppy control signals, smoothing filters are a particular kind of one-pole recurisve IIR filter, usually with parametric smoothing control amount. Signals that go through smoothing filters get a distinct syruppy responsiveness added to it (especially if you set the smoothing amount way up!).
While smoothing filters usually have an association with smoothing out control signals from things like MIDI CC knobs, there are a few other useful things you can do with them. If you put audio-rate signal with note values through it, you get a really lovely legato portamento effect. A gate signal put through a smoothing filter produces an envelope with the most beautiful exponential curves perfect for short percusive sounds.
Tangled Files
smoother.c
and smoother.h
. SK_SMOOTHER_PRIV
exposes the
struct.
#ifndef SK_SMOOTHER_H
#define SK_SMOOTHER_H
#ifndef SKFLT
#define SKFLT float
#endif
<<typedefs>>
#ifdef SK_SMOOTHER_PRIV
<<structs>>
#endif
<<funcdefs>>
#endif
#include <math.h>
#define SK_SMOOTHER_PRIV
#include "smoother.h"
<<funcs>>
The Equation
The one pole smoothing filter employs the following difference equation:
Where is and is . The variables and are the smoothing half time and sampling rate, respectively. is the input signal.
Struct Initialized
sk_smoother
is the struct.
typedef struct sk_smoother sk_smoother;
It is initalized with sk_smoother_init
.
void sk_smoother_init(sk_smoother *s, int sr);
void sk_smoother_init(sk_smoother *s, int sr)
{
<<init>>
}
Struct Contents
struct sk_smoother {
SKFLT smooth;
SKFLT a1, b0, y0, psmooth;
SKFLT onedsr;
};
smooth
is the smoothing parameter, with psmooth
being
a cached variable that is used to check when the smooth
parameter has changed.
smooth
is set to a reasonable default value of 10ms.
psmooth
is set to a different (and also invalid) value of
-1. This will caused the filter coefficients to be
calculated when the compute function is called for
the first time.
s->smooth = 0.01;
s->psmooth = -1;
a1
and b0
are filter coefficients computed from the
smooth
time parameter, with y0
being the filter memory.
s->a1 = 0;
s->b0 = 0;
s->y0 = 0;
onedsr
is the constant 1.0/sr
. This might not actually
be all that helpful since a divide operation is still
happening. Hmm.
s->onedsr = 1.0 / sr;
Setting Smoothing Time
Set the smoothinb time parameter with sk_smoother_time
.
void sk_smoother_time(sk_smoother *s, SKFLT t);
void sk_smoother_time(sk_smoother *s, SKFLT t)
{
s->smooth = t;
}
The smoothing time, also known as the half time, is the amount of time (in seconds) it takes to go to halfway to the trajectory value.
Resetting The Filter
sk_smoother_reset
will reset the smoothing filter, causing
it to snap directly to the value in
.
void sk_smoother_reset(sk_smoother *s, SKFLT in);
void sk_smoother_reset(sk_smoother *s, SKFLT in)
{
s->y0 = in;
}
Compute
Compute a sample with sk_smoother_tick
. This expects an
input signal, and returns a single output signal.
SKFLT sk_smoother_tick(sk_smoother *s, SKFLT in);
This is implementing the equation displayed above. Parameter caching is used so that filter coefficients are only updated when the smoothing amount updates. Doing this shaves off a few redundant CPU instructions.
SKFLT sk_smoother_tick(sk_smoother *s, SKFLT in)
{
SKFLT out;
if (s->psmooth != s->smooth) {
s->a1 = pow(0.5, s->onedsr/s->smooth);
s->b0 = 1.0 - s->a1;
s->psmooth = s->smooth;
}
s->y0 = s->b0 * in + s->a1 * s->y0;
out = s->y0;
return out;
}