[Note: this section of the manual pertains to the recent Beta Release of Minim and not to the version currently being distributed with Processing.]
Summarizing the information mentioned in the Introduction, a synthesis algorithm is what makes sound in music programming in Minim and a synthesis algorithm is made up of one or more sythesis chains. The synthesis chains are made up of UGens patched together, and usually, a synthesis algorithm has exactly one connection to an AudioOutput.
UGens will be talked about in detail in the next section. Here we’re going to cover the synthesis algorithms. Simple synthesis algorithms were shown in the Intro, so we’ll get a bit more complex here.
In thinking about synthesis chains and synthesis algorithms, it’s important to know that the audio is collected sample by sample, starting from the output. Let’s look at an example.
oscillator.patch( filter ).patch( delay ).patch( output );
What happens in this case, is the output tries to collect an audio sampleframe. (A sampleframe is one audio sample for each channel of audio being generated. In the case of stereo, a sampleframe would be 2 audio samples, one for the left channel and one for the right channel.) When it’s ready for a new sampleframe, it asks delay to provide one. In order to generate a sampleframe, delay needs to get a sampleframe from filter, and filter, in turn, needs to get a sampleframe from oscillator. Because of the nature of oscillator, which is a sonud generator, it can simply calculate the values for the sampleframe. That is passed to filter, which calculates its sampleframe. That is passed to delay, which calculates its sampleframe, which is finally handed to output.
Knowing that the synthesis algorithm is output driven is helpful when looking at more complex synthesis algorithms.
As mentioned in the Intro, many of the UGens have multiple inputs, and specifically, the Oscil UGen has amplitude, frequency, and phase input. Here’s an example of a more complex example using the amplitude and frequency inputs of Oscil.
lfo1.patch( oscillator.amplitude ); lfo2.patch( oscillator.frequency ); oscillator.patch( output );
In this synthesis algorithm, when oscillator is asked for a sampleframe by output, oscillator must ask all of its inputs for a sampleframe before it can generate a sampleframe. Oscillator will ask for a sampleframe from one of its inputs, say it’s amplitude input, and then wait for a sampleframe to come from lfo1. Oscillator will then ask for a sampleframe from its other input, frequency, to come from lfo2, before it will calculate a sampleframe.
This is how one can have branching as one heads towards the head of the chains. You can also branh as you head toward the tail.
Perhaps you have a low frequency oscillator that you would like to control the frequency of one tone and also the cutoff frequency of a filter. This is relatively easy because a single UGen can be connected multiple times as an input to other UGens. The following is one implementation of that.
summer.patch( out ); noise.patch( lowPass ).patch( summer ); oscillator.patch( summer ); LFO.patch( oscillator.frequency ); LFO.ptach( lowPass.cutoff );
Here we create a summer patched to the output, generate some noise and pass it through a low pass filter before adding it to the summer, and then patch an oscillator to the summer, too. An LFO is then patched as described in the paragraph above.
It is important to note that if a UGen is patched to the input of more than one other UGen, as LFO is in the example, then the multiple-connected UGen expects to get a sampleframe request from each of the UGens after it in the chain. If one of the output UGen branches never reaches the AudioOuput for some reason, then that UGen will never ask for a sampleframe, and the multiple-connected UGen will generate new sampleframes slower than desired.
In the next section we’ll describe how UGens work and start suggesting some of our examples to look at which tie all of these ideas together.
For those who want to go a little more in depth with patching, when one patches to a UGen or the input of a UGen, the patch method actually returns the UGen object, so the above code could also be written.
noise.patch( lowPass ).patch( summer ).patch( out ); LFO.patch( oscillator.frequency ).patch( summer ); LFO.patch( lowPass.cutoff );
It is a more concise, but perhaps less readable way to create the synthesis algorithm.