To construct a basic grain to use in granulation we just need to recall how does the playback object work (in this case, play~).
Play~ can be set into motion via a message (in the format ‘[start] [from] [to] [duration]‘) or by a signal (line~ ramps or oscillators, or any type of signal really).
In granulation, we want to repeatedly playback a small portion of the buffer~, therefore we need to know the starting point in the buffer~, the ending point (that is, the size of the grain), and the actual speed and direction of playback.
By using a phasor~ signal, we can achieve that in the most straightforward way. Phasor~ produces a signal that ramps from 0 to 1 and that is easily scalable to the value we require: from 100 ms in the buffer~ to 150 ms, can be done by multiplying the phasor~’s signal by 50 and adding it to 100. We will thus obtain a signal constantly ramping from 100 to 150.
That is exactly what we want: to play a grain from a position in the buffer until its total duration.
It is possible to obtain the right playback speed by acting on the playback frequency of phasor~ for that particular length of the sample. It has to be related to the sample rate used in Max and to the size of the grain. By simply dividing 1000 (1 second, 44100 samples at 44.1 kHz sample rate) by the desired size of the grain, we obtain a ratio/frequency which can be used to control playback speed of phasor~ signal, to match the exact original pitch of that portion of the sample.
We can thus control the playback of any portion of the file, set it on loop, and we are also able to act on its pitch by adjusting as we like the rate of phasor~.
The next step in granulation is making sure that everything is synchronised and filtered through an amplitude window so that any incongruences in the audio are filtered out.
The best way to synchronise an envelope is to use phasor to control that too, so that each time the grain is played back from beginning until the end the envelope follows precisely that. To do so, we connect phasor~ into a wave~ object set on a specific buffer~ that contains a small envelope shape. The envelope then acts on a multiplier and makes sure that the audio is faded in and out precisely.
Drawing an envelope
Regardless of the buffer size, phasor~ will sync the reading of the grain in the main buffer with the reading of the envelope, matching their respective starting and ending points.
You can draw your desired envelope just by sending a function message to the buffer~. For example, here in the picture above I used ‘fill 1’ to fill the 11.61 ms of the buffer all with amplitude values 1. Then I told it to also shape all those values in a hanning curve (11.61 ms buffer~ should be approximately 512 samples- just my preference). You can also try to apply other shapes, like by sending the message ‘apply triangle’, you will obtain a triangular shape, and so on. In Max’s buffer~ help file you find more examples of these functions. You can also use waveform~ object in draw mode and draw the envelope by hand, as shown in the next picture.
Synchronising all elements
The envelope, run by phasor~ through the object wave~, can then be used to synchronise all the information sent to the playback object. That is essential as you don’t want to change the pitch or the size of a grain middle-way through its playback. That will cause all sorts of incongruences and would be unreliable and unplayable.
The object sah~ (sample and hold) is very useful for that. It takes an incoming signal and stores it temporarily, releasing it only when another signal has given the ‘give-way’ message. The envelope is the ‘give-way’ signal and the incoming signals are the grain size signal and the position in the file respectively.
As you can see in the picture above, the wave~ object, controlled by phasor~, is sending a give-way message to sah~ via the purple patch cords. The give-way message is simply a threshold: when wave~ passes 0.01 threshold, sah~ releases the sample value that is holding in the left inlet.
The two sah~ objects then release respectively the grain size and position in the file information. This will go to form the precise scaling factor for the phasor~ to control the playback object. They are coloured in green in the picture. The grain size goes to be multiplied to the phasor~ 0-1 signal ramp; the position in the file is instead added just afterwards, to specify where that particular length of grain has to be played from.
The grain size is also sent directly to set the rate of the phasor~, that is because we want to match exactly the correct playback pitch. In the picture above is shown in blue coloured patch cord.
Final touch
The whole point in using granulation is that we can be able to access pitch, size, and position of a grain in any audio sample to manipulate and play with it in several combinations. With the previous explanation we can control the grain size, its position in the file, and we made sure everything is synced through an envelope which also fades in/out the sounds to avoid audio clicks.
The last thing to add is the pitch control. Simply adding a multiplier to the phasor rate would do the job, so that we can still keep a precise sample rate-phasor~ ratio relationship but also manipulate it to do several pitch effects.
I have added to the previous patcher those orange coloured objects. It is just a multiplier that multiplies the phasor-grain ratio by a value: if to 1, pitch would stay the same, if to 0.5 it will sound one octave lower, if to 2 one octave higher will be played back, and so on. The extra two sig~ objects are added to convert everything into signal, so that they can be changed at any time.