From Part 1, we have an oscillator. But we need to broaden it to allow scaling of harmonic content based on pitch so that we have all the harmonic content we need at the low frequency end, and, as we move up, eliminate those harmonics that would be above the the range of hearing and mirror (alias) back into the audible range.
How many harmonics do we need?
To start, we pick a lowest wavetable frequency. Let’s say 40 Hz (it could be 80 Hz, it could be 20 Hz—it could be 1 Hz). The highest harmonic that fits under half our sample rate is 551 (22050 / 40 is 551.25). So, we could make a sawtooth table of 551 harmonics. The top harmonic would be only 10 Hz from the aliasing threshold—Nyquist. If we shift that wavetable up one octave to 80 Hz, the top harmonic would double—which means it would alias back down to 20 Hz. And the top two hundred harmonics would be below 16 kHz…Ouch! That means as we glide the wavetable frequency from 40 Hz to 80 Hz, we’ll be inundated with harmonics aliasing downward throughout the audio range.
Of course, we could go the other direction, using that table from 40 Hz downward (and each higher table handling one octave downward similarly). But that means that as we shift down an octave, all of those harmonics at the top of the audio range will shift down to half the audio range—our highest harmonics would now top out at about 11 kHz before the next table takes over and restores harmonics to the top of the audio range. We’ll hear this shortcoming.
OK, we can cheat some and go for the middle ground. Most people don’t really hear to 20 kHz—adults are lucky if they can hear 14 kHz. If we go for 368 harmonics, that puts the top harmonic at 14.72 kHz for 40 Hz, and the alias would fold back to 14.66 kHz (44100 – 14720 x 2). I think this will work fine for me—I did a quick and dirty check a while back and could hear a 14 kHz tone, but not 15 kHz. The rest of you are on your own. Just kidding (and it ends up my hearing’s not that bad anyway—more later, in the “end notes” installment). Instead of requiring each subtable to cover an octave, we could use twice as many and have them cover half of an octave. Or an even smaller increment. Or we could just oversample the oscillator and expand our shallow frequency headroom greatly, from a few kHz to tens of kHz.
I’ll continue to develop this oscillator in octaves in order to keep the explanation simple (and because I’m going for “good” quality, to leave you to decide if you can sacrifice some quality for performance or memory, or whether you want to improve quality), but the extension to use more closely-spaces subtables is trivial. Oversampling 2x is also easy, but will complicate the explanation—you’re on your own if you want to go that way, but it’s a good learning exercise.
First, let’s back up and figure out how long our tables need to be. Recalling that we need to sample a signal at greater than twice the highest frequency component, that means that for 368 harmonics, we need at least 368 x 2 + 1 samples, or a table length of at least 737 samples. But also remember that we’ll be using linear interpolation, so we need to oversample for good results. That means at least 2x, for 1474 samples.
We have good reasons to choose a table length that’s a power of two, however. There are optimizations we can do that take advantage of binary arithmetic, such as zero-cost wrapping of the table index for fixed-point indices, for instance. Plus, we get a huge boost in building these tables if we use the FFT, compared to summing sine waves individually. Also, we’ll definitely want to use the FFT if we let users define their own waves—and the FFT is best suited for tables whose lengths are a power of two. From 1474, our next highest power of two is 2048, which gives us a bit more oversampling as well. Memory’s cheap, so no problem going bigger, but let’s see where this gets us.
The bottom subtable will be used for that first octave and all pitches below. The second subtable takes over from 80 Hz to 160 Hz, and so on, as frequency doubles (and pitch goes up one octave). We use a suitable, progressively smaller range of harmonics for each table as we go up—dropping the upper half of our harmonics for each higher octave. The top table will be a sine wave, always, so we can just add tables until the last one has one harmonic.
Next: let’s hear the results in Part 3