The digital state variable filter was described in Hal Chamberlin’s Musical Applications of Microprocessors. Derived by straight-forward replacement of components from the analog state variable fiter with digital counterparts, the digital state variable is a popular synthesizer filter, as was its analog counterpart.
The state variable filter has several advantages over biquads as a synthesizer filter. Lowpass, highpass, bandpass, and band reject are available simultaneously. Also, frequency and Q control are independent and their values calculated easily.
The frequency control coefficient, f, is defined as
where Fs is the sample rate and Fc is the filter’s corner frequency you want to set. The q coefficient is defined as
where Q normally ranges from 0.5 to inifinity (where the filter oscillates).
Like its analog counterpart, and biquads, the digital state variable has a cutoff slope of 12 dB/octave.
The main drawback of the digital state variable is that it becomes unstable at higher frequencies. It depends on the Q setting, but basically the upper bound of stability is about where f reaches 1, which is at one-sixth of the sample rate (8 kHz at 48 kHz). The only way around this is to oversample. A simple way to double the filter’s sample rate (and thereby double the filter’s frequency range) is to run the filter twice with the same input sample, and discard one output sample.
As a sine oscillator
The state variable makes a great low frequency sine wave oscillator. Just set the Q to infinity, and make sure it has an impulse to get it started. Simply preset the delays (set the “cosine” delay to 1, or other peak value, and the other to 0) and run, and it will oscillate forever without instability, with fixed point or floating point. Even better, it gives you two waves in quadrature—simultaneous sine and cosine.
Simplified to remove unecessary parts, the oscillator looks like this:
For low frequencies, we can reduce the calculation of the f coefficient equation to
Here’s an example in C to show how easy this oscillator is to use; first initialize the oscillator amplitude, amp, to whatever amplitude you want (normally 1.0 for ±1.0 peak-to-peak output):
// initialize oscillator
sinZ = 0.0;
cosZ = amp;
Then, for every new sample, compute the sine and cosine components and use them as needed:
// iterate oscillator
sinZ = sinZ + f * cosZ;
cosZ = cosZ – f * sinZ;
The sine purity is excellent at low frequencies (becoming asymmetrical at high frequencies).