Thursday, 5 January 2012

The Basic Waveforms

Now that we've dealt with the impulse train it's time to look at other periodic waveforms that often form the basis of more advanced sound synthesis. They're also great by themselves if you're into chiptunes like I am.
I tend to think of sound in terms of the spectrum (the continuous Fourier transform) whenever it makes sense so I regard the impulse train as the second simplest* waveform after the pure sine wave. Most of the practical waveforms lie between these two extremes and the simplest way to go between them is integration.
Starting with the impulse train:
Blit220 by frostburn

if we simply integrate we will get something like this:
which isn't a periodic waveform. It's actually the floor function. What we need to do is to DC-block our wave forms before integration to keep the integral from "blowing up". So we subtract 1 from the impulse train, integrate and end up with:
We could take this as the definition of the sawtooth wave but I like my waveforms normalized, DC-blocked, having a value of zero at t=0 and with a positive derivative at the origin so my definition of the saw wave is:
saw(t) = 2(t - floor(t + 0.5))
Saw220 by frostburn

Subtracting two saws 180° apart gives us a square wave:
Square220 by frostburn

Integrating this we get a triangle wave:
Triangle220 by frostburn

We could also subtract two saw waves with different phase shifts to get different kinds of rectangular waves. In terms of normalization there are two ways that make sense. The simpler one is
pulse(t, duty) = bool(t - floor(t) < duty),
a wave that is 1 for the duty cycle and 0 otherwise:
Pulse08 220 by frostburn

The second normalization is DC-blocked and maintains the loudness of the signal, that is the energy, that is the integral of the square of the signal over time:
square(t, duty) = sqrt( (1 - duty)/duty ) if t - floor(t) < duty
-sqrt( duty/(1 - duty) ) otherwise
Unfortunately the loudness normalized version blows up when duty approaches zero or unity. In some cases it may make more sense to just use the difference of two saw waves to get a DC-blocked well behaving square wave.
The width modulated triangle wave is an easier case because it maintains loudness and peak amplitude at the same time:
triangle(t, width) = 2x/width if x < width/2
-2y/(1 - width) otherwise,
where x = t - floor(t + 0.5), 
y = t - floor(t) - 0.5.
Triangle08 220 by frostburn

As an interesting detail this definition of triangle reduces to saw at width=1. Now if the square waves integral deserves to be called basic then so does the integral of the saw wave, the parabolic wave:
par(t) = 1/2 - 6 (x - floor(x + 0.5))2,
where x = t - 1/sqrt(12) is shifted so that par(0)=0 and I've already normalized the integral to have unity peak amplitude.
Par220 by frostburn

Integrating and normalizing once more we get the cubic oscillator:
cub(t) = sqrt(27) x(1 - 4x2),
where x = t - floor(t + 0.5).
Cub220 by frostburn

We can go on like this forever and once we're done we have the sine wave:
sine(t) = sin(2π t)
Sine220 by frostburn


While these waveforms are simple and pretty straightforward to implement and use they suffer from aliasing. Aside from the impulse train I rendered the sound clips at 352800 Hz sampling rate and resampled them down to 44100 Hz in Audacity. The careful listener may still be able to pick up a few sampling artifacts even at 8x oversampling.
In the next post we'll learn a little trick to reduce aliasing while also avoiding the Gibbs phenomenon.


*) One might even say that the impulse train is the simplest wave form because its Fourier transform is another impulse train in terms of the spectrum.

No comments:

Post a Comment