MSP Polyphony Tutorial 3: Granular Synthesis
Click here to open the tutorial patch: 03hGranularSynthesis.maxpat
In this tutorial we'll look at using the poly~ object to generate large amounts of polyphony in order the play the contents of one buffer~ of sample data. We'll leverage the ability of MSP to play sample data from the same buffer~ at multiple arbitrary speeds and time points to explore the technique of granular synthesis
Put simply, granular synthesis is the use of very short (or, sometimes, less short) sonic events called 'grains' to generate complex textures. While the musical and written literature on the technique is beyond the scope of this tutorial (see Curtis Roads' Microsound (MIT Press: 2004) for a great exploration of this topic), we'll cover the basics here. While classic granular synthesis relies on the use of very small amounts of wavetable data, the technique we'll explore in this tutorial uses sample data taken arbitrarily from soundfiles.
In our tutorial patcher, we'll create an algorithmic playback system based on constrained random values to control the following parameters of a polyphonic sample playback engine: rate, onset point, duration, pitch, amplitude. We'll also look at how adjusting envelopes changes the sonic output.
Experimenting with the patcher
Take a look at the tutorial patcher. There are several numbered areas, each of which controls part of our granular synthesis engine. The patcher area labeled
1 is the grain emitter proper: a metro object schedules and fires
bang messages into a poly~ object that has loaded 100 voices of an abstraction named
2 allows us to check our CPU usage depending on the parameters of our synthesizer. Area
4 set the synthesis parameters - the sample we're using, which area of it to draw from for grains, and the parameters of the grain playback system in the
- In patcher area
1, turn on the audio with the toggle object connected to the dac~. Turn up either one of the gain~ sliders; the other should follow along. At the top of the patcher, click the button object a few times and listen to the results. Sending a
banginto the poly~ object generates a single 'grain' of audio. Turn on the metro object by clicking the toggle at the top of the patcher. The poly~ object in our patcher generates grains: single bursts of sample playback which we can control dynamically by adjusting parameters. The metro and button objects control the grain emitter. Each time the metro fires, it sends a
banginto the poly~, prepended by the
notemessage, which assigns the
bangto the first available voice within the poly~. In addition, each
bangfrom the metro object schedules the next one by adjusting the speed of the metro. The random object generates a random value which is then put through a scale object with a variable output range, defined by the
speedmaxparameters found in patcher area
- With the grain emitter enabled (i.e. the metro object set to run), turn on the metro in patcher area
2. The number box at the bottom of the patcher logic should output a number. Turn off the grain emitter at the top and watch the results. Turn it on again. The adstatus object allows us to control and view aspects of the MSP audio driver currently running. All of the viewable attributes of the Audio Status window (available under the Max Options menu) can be accessed via the adstatus object. The
cpumode of the adstatus object (set by its argument) instructs the object to receive
bangmessages and output the current CPU usage of MSP. Notice that when the grain emitter is turned off, the CPU usage drops to
0.. This is because our poly~ abstraction mutes itself when its playback has finished. When no notes are firing, all of the copies of the poly~ abstraction should be muted.
- In patcher area
3, highlight an area of the waveform~ objects to select part of the buffer~ named
thegrain. Notice that when you drag on either of the waveform~ objects, both of them highlight in the same regions. The rightmost outlet of the waveform~ object allows us to link them together so that you can use more than one of the objects to work with a multi-channel buffer~. Load a different sample using the message boxes above the buffer~ object and highlight different regions of the sample. The highlighted regions of the buffer~ controls where the grain emitter draws its sample data. * In patcher area
4, use the preset object to try out different parameters for our grain emitter, then try entering your own values. The Grain rate number boxes control the speed range of the metro in patcher area
1. The Grain duration controls the ranges for how long each grain plays for inside the poly~. The Grain pitch values provide a range for what speed the grains play at. The Grain amplitude controls set the volume range of the grain emitter, and the Grain slope sets the sharpness of the attack and decay on each grain's envelope. Notice how different densities of grains changes the sound as well as the CPU usage of the grains. Before we look at our poly~ abstraction, notice the effect of longer and shorter grain rates and durations on the CPU usage. Longer grain durations and shorter grain rates result in more voices inside the poly~ being active at any one time - either they are fired more frequently, or they take longer to 'free' themselves, or both. The result is a higher CPU usage.
- In patcher area
1, enable the toggle object attached to the message box labeled
parallel $1. Restart the audio by turning on and off the dac~. Notice the effect, if any, on the CPU. Depending on your computer architecture, you can take advantage of multiple cores in your computer's CPU (or multiple processors if you have a multi-processor machine) by dividing the poly~ object's resources over multiple threads. In essence, this divides the instances of the poly~ object across the different cores or processors of your computer, allowing sets of voices to run in parallel. Depending on your computer's CPU architecture, this may provide a significant boost in performance.
Inside the patch
- Double-click the poly~ object to view an instance of the abstraction named
polygrain~. Take a look around the patcher. The
polygrain~abstraction recieves a single
bang(via the in object at the top of the patcher) and uses it to generate a grain of audio, using the MSP logic at the bottom of the abstraction. The trigger object at the top of the patch clearly sets up the order of events for generating our grain:
First, the thispoly~ object receives a
mute 0 and
1 message in immediate succession. This turns on (unmutes) the signal processing in the instance, and sets it's state to 'busy', so that it won't receive any more messages until the grain is finished.
bang is dispatched to generate a random amplitude for the grain, which goes into the right side of the *~ object labeled 'how loud is this grain?'. This *~ controls the scaling for the output of the line~ object above that sets the grain envelope.
Third, a random pitch is selected which is transformed into a duration multiplier for the line~ objects controlling the playback of the sample and its amplitude envelope. The !/ object divides the incoming pitch into
1., so that a requested pitch of
2. tells the objects downstream to multiply their durations by
0.5 (half as long, and up an octave).
Fourth, a random duration is generated, which sets up the parameters for the line~ objects so that they generate the appropriately scaled and offset values for the grain length.
Finally, a grain is triggered by generating a random start point based on the highlighted areas in the waveform~ object in the main patcher. This
bang eventually generates two messages which command the two line~ objects to generate the playback curve for the play~ object and the amplitude envelope for the *~ objects.
Once the 'envelope' line~ is finished, it sends a
mute the instance and set it to 'free' (
0), so it can receive a new message.
- Under the File menu in Max, select Modify Read Only. This will allow you to unlock the copy of the
simplegrain~abstraction you are viewing. Unlock the patcher, and place 'watchpoints' on some of the patchcords to monitor their values. In the Watchpoints window, you should see how different values in the grain settings in the main patcher translate into values for the synthesis algorithm at work here.
The poly~ object allows you to have a large number of instances of a single, simple MSP patcher. You can use send and receive to communicate to all instances of a poly~ abstraction, which can be distributed across multiple cores or processors with the
parallel message. The adstatus object allows you to access and change aspects of the MSP audio driver; the
cpu argument to the object lets you see how much of your computer's CPU you are using with a patcher.
adstatus - Report and control audio driver settings