There are many ways to make an oscillator. Without looking for further motivation, I’ll propose a wavetable oscillator. Wavetables are a fairly obvious extension of the general playback of digital audio. Such oscillators are easy to understand, and their extreme flexible make them a very popular choice among synthesizer oscillators.
Making a tone from a wavetable
If we start with one cycle of a waveform, we can output the samples one after another, at the sample rate, and repeat the process after getting to the end of the table. Here’s a sine wave, the most “fundamental” (if boring) of all waveforms, at about 440 Hz (“concert A”); I say “about” because a single cycle at 44.1 k Hz would be 100.227 (44100 / 440) samples, so we round to a whole number to give us 100 samples (441 Hz):
Sine wave, 441 Hz
OK, that gives us one tone at excellent quality. We could have a separate wavetable for each note that we want to play, but that wouldn’t let us vary the pitch by arbitrary amounts, such as for vibrato or frequency sweeps. Also, we’d continue to run into the problem of our table needing to be an integer length—a problem that becomes extreme at high frequencies (due to shorter tables).
Arbitrary pitch from a wavetable
A first guess at a solution might be to mimic changing the playback rate on a tape recorder by varying the sample rate. This works quite well, and has the advantage that any errors in the waveform (due to a short wavetable or short sample size) remain harmonically related, so they’re heard as harmonic distortion instead of noise (hiss), and aliasing is not a threat because we’re raising the sample rate to play higher notes. This technique was used in some early digital synthesizers (such as the Fairlight CMI), but has a glaring weakness: we can’t multiplex this type of oscillator—each independent voice requires its own variably-clocked DAC.
However, we can do the equivalent shifting with DSP by using sample rate conversion techniques (sometimes called resampling). Because we can resample a wavetable at multiple rates with DSP, we can generate multiple tones digitally, before sending them to a common DAC.
Basically, we simulate the different clocking rates by using a fixed clock and scanning through the wavetable at a different rate. (We’ll use the CD sample rate of 44.1 kHz for our tests—higher rates are easier, since they give more audio headroom before aliasing, making 44.1 kHz a good test as we develop the algorithm.) So, instead of outputting the first sample, then the second—stepping through the table with an increment of 1—we can step through with a smaller increment for lower pitches and a larger increment for higher pitches.
But we need to decide which sample to use for a fractional offset. The simplest choice is to truncate the fractional index by taking only the integer portion. This has been done in popular instruments of the past, especially early samplers. I’ll cut straight to a recommendation and suggest that we use linear interpolation—it’s not much more computationally expensive, and gives us enough improvement in sound quality to be worth the effort. We’ll discuss linear interpolation more later.
Here’s our sine wave at a different pitch, using a fractional increment and linear interpolation—middle C (261.626 Hz):
Sine wave, 261.626 Hz
That sounds promising—and note that fractional increments let us get our target pitch exactly this time. To prove it works at any pitch we’re interested in, here’s the same technique used with an exponential sweep, changing the wavetable index increment at each cycle to cover a wide range:
Sine sweep, 20-20,000 Hz
That sounds like we may have solved the problem of creating any pitch we want from a wavetable! But let’s test further…
A classic synthesizer waveform
Sine waves are boring. A staple for classic synthesizers is the sawtooth wave—a harmonically rich waveform that’s excellent for subtractive synthesis. A single cycle of a sawtooth climbs in value from the lowest value to highest, then resetting instantly to the lowest. We can’t generate a perfect sawtooth in a wavetable (we can’t reset instantly, for instance—we must wait till the next sample output), so the proper thing to do is to build a band-limited sawtooth from sine wave harmonics. A band-limited sawtooth wave is made up of a sine wave for the first harmonic, a sine at twice the frequency but half the amplitude for the second harmonic, a sine three times the frequency but a third of the amplitude, etc. Because our wavetable is 100 samples, we can fit a sawtooth of 49 harmonics.
Here’s what a sawtooth of 441 Hz sounds like:
Sawtooth wave, 441 Hz
And here we sweep the range as before:
Sawtooth sweep, 20-20,000 Hz
Yikes! At the higher pitches, the tone is too harmonic-rich, causing strong aliasing. Also, notice that at the lower pitches the sawtooth starts out sounding a bit dull, lacking the highest harmonics.
Clearly, we do not have a suitable wavetable oscillator yet. We need the ability to scale the harmonic content to the pitch required—starting with a wavetable tailored to the lowest pitch we want to produce, and reducing harmonic content as we move up in pitch before it has a chance to fold back as aliasing at half the sample rate.
Next: working on a solution in Part 2
I’m having issues working with variable cycle wavetables. Most interpolation codes work for a wavetable that is exactly 1-cycle, but there is a lot of aliasing and clicking if the wavetable is larger than a cycle by just a little.
Yes. If you can’t make the wavetable the exact length of a cycle (maybe one cycle is a fractional number of samples in length, for instance), you need to sample rate convert the audio so that one cycle of the audio is the length of a wavetable.
Thank you for this excellent introduction. I’ll be reading more of your articles.
Would you mind explaining “We can’t generate a perfect sawtooth in a wavetable (we can’t reset instantly, for instance—we must wait till the next sample output), so the proper thing to do is to build a band-limited sawtooth from sine wave harmonics.” in basic terms?
I thought the wavetable can be arbitrary, e.g. 0,1,2,3 – so are you implying that this “ideal” sawtooth cannot be reproduced and as such needs to be bandwidth-limited in order not to cause issues with the physical bandwidth?
You can put anything in the table, but the problem is in resampling it to another pitch. Resampling requires interpolation that must also maintain the requirement that all frequency components are below half the sample rate. If you have a sawtooth, with all harmonics allowable, and you want to pitch it up but stepping through with an increment of less than one table sample, it will alias. Of course, you could use one table and a resampling algorithm that uses filtering to maintain the frequency limit, but that is processor intensive—multiple wavetables let you pre-compute filtered tables and in case allows us to use cheap linear interpolation.