Wavetables continued
How function tables are read:
A standard oscillator will repeatedly sweep through a wavetable, based on a note’s specified frequency. It scans through the index numbers as values from F(n=0) (if the phase is set to start at 0) to F(infinity mod the table size), so the scan wraps around at the table size, as depicted in the circular graph figure coming up. Here’s where it gets interesting. The table values are resampled at the project’s sampling rate, as depicted in the figure below.
This means that depending on the table size and the sampling rate ratio plus the oscillator frequency, some indexed values may be sampled twice, or some index values may be skipped. If the frequency of a note event is 1 Hz, then the oscillator will scan through the table once every second, but resample the table at the sampling rate. However, if the note is 2 Hz, what then? Will it read through the table twice as quickly? Well, yes and no. It will scan through the table two times in a second. But since the sampling rate remains constant, it will skip twice as many indices when resampling. For fractional ratios, each scan may wrap around to a different starting index point in the table. And of course at audio rate frequencies, it may only sample the wavetable twice for near the highest possible Nyquist frequency.
Many sampled table values should be fractional, as particular resamples fall in between the index integer values (for example, F(n= 3.5)). The simplest of oscillators truncates the fractional index value to the nearest existing index value, but better solutions are mentioned below. A more technical way to look at how a wavetable will be read by a program is to compute the index number F(n) increment of each sample. The formula for that is:
At a large modern table size of 4096 and a modern sample rate of 44,100, here is the resampling increment for several pitches (rounded off). Note how the higher the frequency, the greater the jump of indices (F(n) increment) for each sample. Also added are the number of samples taken per table scan, determined by dividing the sampling rate by the oscillator frequency:
Wavetable Resampling Increments | |||
---|---|---|---|
Pitch |
Frequency (Hz) |
F(n) increment |
Approximate number of |
A | 220 | 20.43 | 200 |
A | 440 | 40.87 | 100 |
D# | 19912 | 1849.43 | 2* |
Another way of looking at this process would be to view the increments measured in terms of phase angle change. The formula would then be:
The animated illustration below shows a wavetable visualized as a circle with the following parameters using the two formulas above. Note it takes two times through the table to sample F(0) again because of the specified increments and the fractional non-integer samples per cycle:
Osc Frequency |
Table Size |
Sample Rate |
Index F(n) Increment
|
Phase increment
|
Samples per cycle
|
---|---|---|---|---|---|
9800 Hz | 128 | 44100 | 28.444… | 80° | 4.5 |
Some pretty stairstep-y, noisy waveforms may come out of the oscillator reading the wavetable, particularly at higher frequencies. There are two solutions to this issue to eliminate unwanted noise. The first is to use larger table sizes, particularly when memory concerns are not an issue. And the second is to use what is called an interpolating oscillator that will compute smoother increments between indexed values—so index F(2.5) might output the interpolated value midway between index 2’s value and index 3’s value. Interpolating oscillators are normally twice as slow to compute as non-interpolating, truncating ones, but they are much quieter. A good example of this is the MAX cycle~ object which defaults to an index length of 512 (plus the addition of one wrap-around point, so really 513), but is an interpolating oscillator. A larger cycle~ table size can be user-specified if desired.
This ends our discussion of synthesis language wavetables.