In Part 2, we looked at how to use subtables designed to cover one octave each, removing upper harmonics for each higher octave to avoid aliasing. Here’s a view of the nine wavetables for our sawtooth oscillator, with the starting frequency and number of harmonics for each octave:
The table size for each subtable is 2048 samples of single precision floating point. We’re using a double precision floating point phase accumulator. Here’s the result, with an exponential sweep from 20 Hz to 20 kHz:
Sawtooth sweep 20Hz-20kHz, 20s
That sounds…good! Some observations:
- The wave starts out a little short of harmonics—we knew it would, because we built our lowest table for 40 Hz and up.
- You probably can’t hear the very end of the sweep, because the frequency is too high.
- If you play it back on a good audio system and listen carefully, you can hear at least two “ticks” in the audio, nearing the end of the sweep, about 2 seconds apart.
The ticks are due to the abrupt change in energy as we switch to the next table with fewer harmonics. It’s much easier to notice it on in the high range, since the relative power lost is larger (stronger harmonics are dropped). This might be especially noticeable with a strong vibrato that spans tables on a very high note. You can see the transitions in our sweep:
It would be easy to fix—with a higher sample rate, or by crossfading the switch. Or, you can ask yourself how often you’ve ever played sustained notes that high…with strong vibrato…soloed in a mix…with the synth filter missing. The fact is that all synthesizer oscillators make tradeoffs such as this. Either way, we’ll carry on and keep this oscillator simple and easy to understand.
Other waveforms
So far, we’re building the waves computationally. We could sample a single cycle of anything, resample it into a wavetable length of our choice, then use an FFT to get the spectrum, and modify the spectrum as needed for our additional octave tables. This is where the wavetable oscillator has a huge advantage over analog oscillators—any combination of harmonics is possible. For now, we’ll just deal with completing the classic synthesizer waveforms. For most analog synthesizers, that means a sawtooth and a variable-width pulse (rectangle) wave.
The Minimoog had just three choices of pulse width, but most synthesizers have voltage control of pulse width for pulse width modulation (PWM) effects. At first it might seem that we’d need to generate many variations of pulses, but fortunately we can create the same thing by subtracting one sawtooth from another at the same frequency but different phase. Then we get our pulse width modulation by controlling the phase offset. Here’s a square wave (50% pulse) generated using this method:
Square wave sweep 20Hz-20kHz, 20s
The triangle wave was also found in the Minimoog, and is routine for modular analog synths, but was sacrificed in most classic non-modular analog synths because it’s not used as often as sawtooth and pulse. Compared with a sawtooth, a triangle has only odd harmonics, and the harmonics fall off as the inverse square of the harmonics number (and they alternate in sign as well), giving it a hollow sound like the square wave (also odd harmonics), but much more mellow sounding due to the fast rolloff of harmonic energy.
Triangle wave sweep 20Hz-20kHz, 20s
We’ll look more closely at how the oscillator is implemented, as well as give additional observations about wavetable oscillators, in upcoming articles.
Fantastic series, looking forward to part 4. Will you be providing C code?
Coming very soon, just need some editing time on the article…with source code in C++ 😉
Hi Nigel
Fantastic series of articles on wavetables.
Im just wondering what filter you use for creating your series of bandlimited waves?
Cheers
John
Hi John,
By now you’ve probably already found the answer in subsequent articles in the series—especially Replicating Wavetables. That is, I use the FFT/iFFT to build tables with the correct number of harmonics.
Nigel
Hey Nigel
Your right I read your other articles and see where you obtain your band limited waves.
Not sure if you can answer this question but ive been implementing a wavetable oscillator and used Matlab FFt functions to precisely filter waveforms at 24kHz, 12kHz, 6kHz etc down to 187.5Hz based on your articles. For some reason I am still getting slight aliasing on the last two waves.
Have you seen or experienced this before?
Thanks for any help.
John
No, it would be tough to say without seeing exactly what you’re doing.
What if the sweep (i.e modulation) is faster? i.e. pass from lower wt index to higher wt index?
How would you manage it without introuce trash and/or clicks?
In few words: what’s the best way to manage FM?
There is no simple way. Obviously, it’s a lot more complex when the waveforms are complex, but even the DX7, with sine waves, did tricks like dropping the modulation level are higher frequencies, to minimize aliasing. Oversampling is the basic way with a wavetable oscillator, but you’ll always have limits.
What about mipmaps interpolation? Can fix it?
If the FM or PM results in a frequency that will alias…