Chapter Four: Synthesis

14. A Digital Synthesis Language Sampler | Page 3

Wavetables

This section is not really crucial information for creating music with synthesis languages, though it may sometimes come in handy, but it is provided here for the curious.  Feel free to skip.

Most synthesis languages use numeric data stored in two-dimensional indexed function tables called wavetables.  These tables are created, stored and accessed, even if they are populated by running a mathematical formula, for a wide variety of musical uses rather than computing each sample through mathematic computation on the fly, which was not possible early on. You may have heard the term wavetable synthesis. Wavetables can contain single-cycle periodic data to be accessed by oscillators, or contain digitally-outlined shapes to be used for envelopes, windowing functions for calculations like phase vocoding, a sequence of pitches for ostinati, or even contain entire audio samples that can be accessed linearly or non-linearly.

Most languages require wavetable sizes to be powers of 2 (512,1024, 2048, etc.).  The indexing is normally 0-based, so the index positions of a 512-sized table may be integers from 0-511, etc., and the index numbers are referred to as F(n), with n being the particular index #.  Finally, many languages have a special wrap-around use for the final value such as interpolation from the last back to the first value. In the case of Music V the usable values are 511 indices, while csound can use a power of 2 and then add an additional 1 [called the wrap-around guard point].  Most wavetable’s stored values are computed as high-precision floating point values between 0 and 1, or -1 to +1, so either unipolar or bipolar.  When used with an oscillator for example, the indexed value will be multiplied by the amplitude specified in the note event.   Many languages will provide a flag for a normalization to the table to its proportionately maximum values, but if you are storing say specific pitches for a sequencer, you'd want to turn this feature off.

Most synthesis languages or environments store these tables in computer memory.  While most computers now have an excess of memory needed for most audio functions, this was hardly the case early on with second-generation mainframes and well beyond.  While it might seem intuitive in the orchestra-score paradigm to create the wavetables needed in the orchestra along with instrument definitions, Mathews brilliantly decided early on with MUSIC-N that the tables should be created as part of the score, where they could be created only at the score time they were needed and then destroyed to free memory up for other tables when they were no longer needed (definitely not something you’d want to do to orchestral players in real life).

How table functions are created: 

There are many possibilities for creating the content of wavetables.

1. The first would be to manually enter each value.  This could be quite useful for storing and recalling a sequence of frequencies or amplitudes, for example, to be used as data. For large waveform tables, this could become quite tedious.

2. A very common method, particularly when a table might be used for an enveloping function, or for simple linear waveforms is to specify break points by an alternation of index number and value statements, with straight or exponential line values filled in in between by the program without the need to fill in each indexed value.  For example, for a size 512 table, [0 0 128 1 384 1 511 0] would fill in a rising linear values from 0 at index 0 to 1 at index 128, remain at 1 1 until index 384 and then descend back to 0 by index 511.

table envelope

3. A third method for populating a table would be to have the program compute the index values from a trigonometric function, such as sine or cosine. Or it may be populated by additive synthesis subroutines whereby the sum of user-defined weighted sines can be specified.  [f1 1 .5. 0 .25] could mean fill table 1 with the composite values of a full-strength fundamental, half-strength second partial, no third partial, and quarter-strength fourth partial.

4. Another method would be to read in an existing audio file sample by sample.  One could read in a single cycle of an instrument timbre, for example a guitar note, to be played by an oscillator.  Or one could read in a much longer audio file (or even record into one in real time, like MAX's [buffer~] object) to be played by more slowly reading through the index #’s with a line function, or random access of segments, or yes, even a slow sawtooth oscillator for looping.

Different languages provide different methods for generating wavetables, but a look at the csound GEN routine manual page gives an idea of how many are available—fifty-two at last count!