For this assignment (and subsequently improved in the final project), I
decided to implement analog-style band-limited oscillators, a la Stilson and
Smith (1997). It is believed that the Novation Supernova series of synthesizers
use this method. I plan to eventually implement these oscillators in C++,
as part of MSTk++.
I implemented a BLIT algorithm as a macro,
using a Discrete Summation Formula, because the sum-of-cosines generator built into CLM
was giving me scaling problems when sweeping the frequency. You will need this file to
run the instruments mentioned below.
I first implemented a sawtooth oscillator,
via integration of a band-limited impulse train (BLIT).
Next I made a square wave via integration of a
bi-polar BLIT, which includes a pulse-width parameter. Due to linearity of integration,
this is mathematically equivalent to generating square waves by subtracting
one phase-shifted sawtooth from another. By scaling the negative BLIT component (between 0 and 1), I have
added a "morph" control between saw and square.
I next made a triangle ramp oscillator, via
integration of the square wave. In this case, the pulse-width controls the
slope of the triangle wave, smoothly varying the slope between a ramp (quasi-sawtooth)
and a triangle shape. The morph parameter transitions between parabolic and
ramp waveforms. In the same manner as the pulse-wave, this is equivalent to
summing two parabolic (integrated sawtooth) waves. This instrument is especially
prone to DC glitches at the startup, due to the double-integration.
Again due to the linearity of integration, if two BLITs are summed, then only one integration needs to be performed
to get a "super-saw." This method is more efficient than simply mixing
two saw waves, and has exactly the same cost as generating a single square wave.
An interesting idea would be to subtract two sawtooth waves, to get some kind of "super-square". I haven't tried this yet, but it should have some kind of wacky PWM.
This patch is reminiscent of the Roland Alpha Juno series, made famous in the early 90's
"hardcore" rave scene:
hoover.wav
Hard-Sync
For the final project, I also generated bandlimited hard-sync sounds. MinBLEPs
are good for this (see below), but I haven't quite figured out the implementation.
I found some C code that creates bandlimited sync sounds, using two bandlimited
wavetable oscillators and a cos^2 window. The trick is to calculate two out-of-phase
waveforms, and then crossfade them with the window. Soft-sync can be done
by only resetting the slave phase when the master phase resets AND the slave
phase is within a given distance from zero. I used the window method with
BLITs, to create a hard-sync CLM instrument: good-sync. The sound is very
realistic if you ask me!
The advantage of the BLIT method, is that calculation is constant for all frequencies. In practice,
it is more efficient to use additive sines for higher frequencies.
Another advantage of the (DSF) BLIT method is that a lowpass filter is built in, by modifying
the harmonics and/or rolloff parameter of the DSF. One problem with the BLIT method is that DC
transients occur when starting up the integration. This can be minimized by creating a lookup table
with integrator initialization values versus frequency (and phase).
Another possible way to reduce processing when doing the phase-shifted and subtract type oscillators (square, ramp)
is to use a comb filter to generated the phase-shifted waveform. However this causes a glitch on the first cycle of the waveform,
and the slight overhead of an interpolated delay line for modulation.
In retrospect, the ramp waveform is not so great as an audio oscillator, but would be good as an LFO.
In this case it would be more effecient to generate a parabolic wave from scratch.
By comb filtering the parabola (as above with the square), a variable-slope ramp wave can be generated. Other Methods
A more elegant method of generating bandlimited waveforms is the MinBlep method,
which uses a wavetable of a pre-integrated minimum phase BLIT, to create
a minBlep (min-phase BL step). In theory it can generate a bandlimited version
of any arbitrary waveform. I have yet to test it out. One disadvantage of
this method is that processor load increases with oscillator frequency, as
more wavetable transitions have to be pasted in. This would be problematic
in a hardware DSP implementation.
On the topic of oversampling, it is speculated that the Nord synths simply generate "naive"
waveforms at a very high samplerate; then downsampling (with a proper LPF) is performed at the output.
This has the added advantage that higher-order modulations will not alias noticably (for instance, ring mod and FM).
Juicy Filters
I found to my surprise that CLM did not have a resonant multi-mode filter
(I am infatuated with the juicy sound of a low frequency sawtooth through a
resonant highpass filter!!). So I implemented an oversampled State Variable Filter (aka Chamberlin filter). It
is oversampled in order to make the cutoff frequency more accurate at high
frequencies. It will operate as a highpass, lowpass, bandpass, or notch filter.
The same algorithm is implemented in my PD extern, svf~.
Here is a page with a good explanation of the State Varible filter (with simplification to the Magic Circle oscillator!): StateVar @ Earlevel.com
For fancy effects, it is nice to have two filters, and crossfade the output of the
first filter with the output of both filters in series. This way you
can "morph" between a 12 and 24 dB/octave cutoff slope. I tried to make a with-filter
macro, but ran into some strange problems. This is on my to-do list...