The original wave table articles advocated minimizing the number of tables necessary—one per octave—by allowing a reasonable amount of aliasing. Aliasing that is not only difficult to hear, but is normally be removed in typically synthesizer applications by the synth’s lowpass filter.
But that is simply a choice of the individual wave tables and how much of the spectrum we’re willing to let each cover. We could use more wave tables and allow no aliasing at all.
In addition to the fillTables function, which builds active wave tables. I’ve added fillTables2, which accepts a minimum top frequency, and a maximum top frequency. For instance, we might want to support a minimum of 18 kHz, using a value of 18000 divided by the sample rate, so that harmonics are supported to at least that frequency. If we use 0.5 for the maximum, then no aliasing is allowed. Higher values allow aliasing. For instance, a value of 0.6 allows a top frequency of 0.6 times the sample rate, or 26460 Hz at 44.1 kHz sampling. That’s 4410 above half the sample rate, so aliasing can extend down to 17640 Hz (22050 – 44100). Another way to look at it is to subtract the value from 1 and multiply by the sample rate to get the alias limit, (1 – 0.6) * 44100 = 17640.
Here are some examples. First, the original octave tables. To understand the spectrograms, time is left to right—a 20 second sweep from 20 Hz to 20 kHz of a sawtooth. You can see the aliasing as harmonic frequencies bend down at the top, although the algorithm minimizes the aliasing with advantageous choices of switching frequencies at the highest sweep frequencies, where there is the least masking. This uses ten wave tables to sweep the ten octaves of audio from 20 Hz to 20 kHz.
I think the aliasing is masked pretty well. But if the idea of aliasing bothers you, and you want at least 18 kHz coverage, 34 wave tables will get you this, at 44.1 kHz sample rate:
Now for an asymmetrical example. If you want 18 kHz, but are willing to accept aliasing above 20 kHz, 24 wave tables will do it: