Sound Synthesis Theory/Oscillators and Wavetables
Oscillators and Wavetables
Oscillators

An oscillator is a repeating waveform with a fundamental frequency and peak amplitude and it forms the basis of most popular synthesis techniques today. Aside from the frequency or pitch of the oscillator and its amplitude, one of the most important features is the shape of its waveform. The time-domain waveforms in Fig. 5.1 show the four most commonly used oscillator waveforms. Although it is possible to use all kinds of unique shapes, these four each serve a range of functions that are suited to a range of different synthesis techniques; ranging from the smooth, plain sound of a sine wave, to the harmonically rich buzz of a square wave.
Oscillators are generally controlled by a keyboard synthesiser or MIDI protocol device. A key press will result in a MIDI note value which will be converted to a frequency value (Hz) that the oscillator will accept as its input, and the waveform period will repeat accordingly to the specified frequency. From here, the sound can be processed or manipulated in a variety of ways in the synthesizer or program to enrich or modify the sound further.
Generating oscillator waveforms
Sine wave
As mentioned previously, the sine wave can be considered the most fundamental building block of sound. The best way to generate an oscillator which produces this waveform is to make use of an inbuilt library or function in the system concerned. Many programming languages have standard mathematics libraries with many of the trigonmetric functions represented. A cycle of a sine wave is radians long and has a peak amplitude of , as shown in Fig. 5.2. In a digital system, the generated waves will be a series of equally-spaced values at the sample rate.

With a sample rate of 44100 cycles per second, and a required cycle length of 1 second, it will take 44100 samples to get from 0 to . In other words, we can determine the steps per cycle from cycle length :
Where is the sample rate. Each step will therefore take the following amount in radians:
Where , in the second result, is the same result in terms of frequency. The importance of this is that it is possible to expand it into an algorithm that will be suitable for generating a sinusoidal wave of a user specified frequency and amplitude- effectively the simplest synthesizer possible! A sinusoidal wave can be generated by repeatedly incrementing a phase value by an amount required to reach a desired number of length cycles a second, at the sample rate. This value can be passed to a sine function to create the output value, between the user specified peak amplitude.
Input: Peak amplitude (A), Frequency (f)
Output: Amplitude value (y)
y = A * sin(phase)
phase = phase + (2 * pi * f / Sample Rate)
if phase > (2 * pi) then
phase = phase - (2 * pi)
The most important thing to note about this algorithm is that when the phase value has exceeded it will subtract by one whole period. This is to ensure that the function "wraps" round to the correct position instead of going straight back to 0; if a phase increment oversteps and reset to 0, undesirable discontinuties would occur, causing harmonic distortion in the oscillatory sound.
Square wave
The square wave is cannot be generated from a mathematical function library so easily but once again the algorithm is particularly straightfoward since it is constructed from straight line segments. Unlike the sine wave, square waves have many harmonics above their fundamental frequency, and have a much brighter, sharper timbre. After examining a number of different waveforms it will start to become apparent that waveforms with steep edges and/or abprupt changes and discontinuities are usually harmonically rich.

The square wave is constructed in a very similar fashion to the sine wave, and we use the same approach by cycling through a pattern with a phase variable, and resetting once we exceed radians.
Input: Peak amplitude (A), Frequency (f)
Output: Amplitude value (y)
if phase < pi then
y = A
else
y = -A
phase = phase + ((2 * pi * f) / samplerate)
if phase > (2 * pi) then
phase = phase - (2 * pi)
As is evident there is no reliance on an external function, the square wave can be defined by simple arithmetic, since it essentially switches between two values per cycle. One can expand on this by introducing a new variable which controls the point in the cycle the inital value switches to its signed value; this waveform is known as a pulse wave. Pulse waves are similar in character to square waves but the ability to modulate the switching point offers greater sonic potential.
Sawtooth wave

The sawtooth wave is more similar in sound to a square wave although it has harmonic decay and an appropriately "buzzy" timbre. It is constructed out of diagonal, sloping line segments and as such requires a line gradient equation in the algorithm. The mathematical form:
Where represents amplitude and is the phase. This can be incorporated into the algorithmic form as follows:
Input: Peak amplitude (A), Frequency (f)
Output: Amplitude value (y)
y = A - (A / pi) * phase
phase = phase + ((2 * pi * f) / samplerate)
if phase > (2 * pi) then
phase = phase - (2 * pi)
Triangle wave

The triangle wave shares many geometric similarities with the sawtooth wave, except it has two sloping line segments. The algebra is slightly more complex and programmers may wish to consider consolidating the line generation into a new function for ease of reading. Triangle waves contain only odd-integer harmonics of the fundamental, and have a far softer timbre than square or saw waves, which is nearer to that of a sine wave. The mathematical form of the two lines segments are:
For to radians:
For to radians:
The algorithm then, is similar to the previous examples but with the gradient equations incorporated into it. In the example algorithms presented here it is evident that a range of different waveshapes can be designed, but there is the realisation that the shapes can only be described as mathematical functions. Complex shapes may become very demanding due to the increased processing power for more complicated mathematical statements.
Input: Peak amplitude (A), Frequency (f)
Output: Amplitude value (y)
if phase < pi then
y = -A + (2 * A / pi) * phase
else
y = 3A + (2 * A / pi) * phase
phase = phase + ((2 * pi * f) / samplerate)
if phase > (2 * pi) then
phase = phase - (2 * pi)
Wavetables
There may be a situation or a desire to escape the limitations or complexity of defining an oscillatory waveform using mathematical formulae or line segments. As mentioned before, this could be a concern for processing power, or simply the fact that it would be easier to specify the shape through an intuitive graphical interface. In cases like these, musicians and engineers may use wavetables to be their source oscillator. Wavetables are popular in digital synthesis applications because accessing a block of memory is computationally faster than calculating values using mathematical operations.

The wavetable is in essence an array of N values, with values 1 through to N representing one whole cycle of the oscillator. Each value represents an amplitude at a certain point in the cycle. Wavetables are often displayed graphically with the option for the user to draw in the waveshape he or she requires, and as such it represents a very powerful tool. There is also the possibility of loading a pre-recorded waveshape as well; but note that a wavetable oscillator is only a reference table for one cycle of a waveform; it is not the same as a sampler. The wavetable has associated with it a read pointer which cycles through the table at the required speed and outputs each amplitude value in sequence so as to recreate the waveform as a stream of digital values. When the pointer reaches the last value in the table array, it will reset to point one and begin a new cycle.
Using wavetables
The size of the wavetable and the sampling rate of the system determine what the fundamental frequency of the wavetable oscillator will be. If we have a wavetable with 1024 individual values and a sampling rate of 44.1 KHz, it will take:
seconds to complete one cycle. As previously shown, frequency can be determined from , giving us a fundamental frequency of:
It therefore becomes apparent that, in order to change the frequency of our oscillator we must change either the size of the wavetable or the sampling rate of the system. There are some real problems with both approaches:
- Changing the wavetable size means switching to a different sized wavetable with the same, updated waveform. This would require dozens, hundreds, or even thousands of individual wavetables, one for each pitch, which is obviously totally inefficient and memory-consuming.
- Digital systems, especially mixers that combine several synthesized or recorded signals, are designed to work at a fixed sampling rate and to make sudden changes to it is once again inefficient and extremely hard to program.
- The sample rate required to play high frequencies with an acceptable level of precision becomes very high and puts high demand on the system.
One of the most practical and widely-used approaches to playing a wavetable oscillator at different frequencies is to change the size of the "steps" that the read pointer makes through the table. As with our previous example, our 1024-value wavetable had a fundemental frequency of 43.5 Hz when every point in the table was outputted. Now, if we stepped through the table every 5 values, we would have:
It follows from this a general formulae for calculating the required step size, S for a given frequency, f:
Where N is the size of the wavetable and is the sample rate. It is important to note that because the step size is being altered, the read pointer may not land exactly on the final table value N, and so it must "wrap around" in the same fashion as the functionally generated waveforms in the earlier section. This can be done by subtracting the size of the table from the current pointer value if it exceeds N; the algorithmic form of which can easily be gleaned from the examples above.
Frequency precision and interpolation
We must consider that some frequency values may generate a step size that has a fractional part; that is, it is not an integer but a rational number. In this case we find that the read pointer will be trying to step to locations in the wavetable array that do not exist, since each member of the array has an integer-valued index. There may be a value at position 50, but what about position 50.5? If we desire to play a frequency that uses a fractional step size, we must consider ways to accommodate it:
- Truncation. By removing the values after the decimal point we restore the chosen step size to an integer. However, although this is the most computationally efficient technique, it introduces the greatest lack of precision. For instance, 1.3 becomes 1, and 4.98 becomes 4. The step discontinuity between the values at times 4.99 and 5.00 introduces a distortion in the resampled signal.
- Rounding. If the value after the decimal point is less than 5, we round down, if it is greater than five, we round up. So 3.49 becomes 3, and 8.67 becomes 9. In this case, the computation requirements increase slightly, and the same step discontinuities appear, this time between the values at times 4.49 and 4.50.
- Linear interpolation. This is the method of drawing a straight line between the two integer values around the step location and using the values at both points to generate an amplitude value that interpolates between them. This is a much more computationally demanding process but introduces much greater precision.
All these processes introduce discontinuities and distortion into the wavetable, which become increasingly obvious with a decrease in the size of the wavetable. By increasing the wavetable size, the precision of the above processes becomes greater and will result in a smoother 'fit' to the idealised, intended curve. Naturally, large wavetable sizes result in greater memory requirements. Some wavetable synthesizer hardware designs prefer table sizes that are powers of two (128, 256, 512, 1024, 2048, etc.), due to shortcuts that exploit the way in which digital memory is constructed (binary).