This series of tutorials first appeared on my CreativeSynth.com website between 2001 and 2002. Due to their popularity (especially amongst new Max users), I have moved them to the Cycling74.com website. Read the introduction.
Now that we have a useful (but basic) monosynth, we have to figure out where to go next. For my money, the biggest limitation of our little monosynth is that it is, well, a monosynth. In the next two tutorials, we will turn our single-voice synth into a 16-voice polysynth. It won't quite rival a Prophet 5, but it should be a good way to learn the basics of polysynth creation.
Prior to Max4/MSP2, creating a polysynth was a bit of a pain. You had the poly object, but it did nothing for DSP management - which meant that you were creating a bit of a CPU hog. MSP2 changed all that with the introduction of the poly~ object, which is a "patch manager" object for polyphony handling. What is a "patch manager" object? It's an object that controls access to a sub-patch. In the case of poly~, it controls inputs and outputs to a "voice" patch, creates the necessary instances of the voice object, controls voice handling and stealing and maintains CPU-limiting DSP muting.
Yup - an excellent tool to help in poly-ing a synth. In this tutorial, we will look at creating a "voice" patch. Here is the voice patch we will create from last tutorial's synth:
The really new stuff is the poly~ input and poly~ output sections. The "Oscillator Stuff" section is unchanged, and the "Filter/Envelope/VCA Stuff" section are those sections from the previous tutorial's patch, with all of the UI elements (knobs, filtergraph) removed. Since the poly~ object will hide the UI of the subpatch, we cannot have any user interface in the voice patch.
The input section utilizes a new object: the in object. The in object determines the number of inlets that the poly~ object will have in the main patch - it is also where you will get your user interface values. Since we need to establish what each of the inlets will provide, I've added comments next to each of the in objects: in 1 will be midi note information, in 2 will be for the filtergraph list, and in 3 & 4 will be the attack and release values.
The MIDI input (in 1) will needs to be processed - frequency determination is only concerned with note-ons, while the amplitude envelopes (controlled by ddg.velamp) needs to see note-offs as well. By unpacking the MIDI information, we can split and use the note and velocities as are necessary.
The output of all the in objects are routed to their appropriate locations:
ΩThe note number from the stripnote object determines the oscillator frequency (with the mtof object) and the filter list (from in 2) goes to biquad~. The raw velocity value, the in 3 (attack) and in 4 (release) values are all sent to ddg.velamp for envelope handling.
Finally, the output section serves two purposed - getting the audio out, and communicating with the poly~ object. Sending audio out is as simple as creating an out~ object. The remainder of the output section is sending information to the thispoly~ object - which provides data that poly~ can see. The object is called thispoly~ since it maintains settings about "this particular instance of the voice patch". For example, if you create a 16-voice synth, 16 instances of your voice patch are created in memory. Your voice patch is responsible for maintaining information about the current state of each instance.
There are two pieces of information that must be maintained by thispoly~: the "busy" flag and the "mute" flag. Busy means that this instance of the patch is currently in use, and should only be used if no other voices are available (and voice stealing is enabled). Mute means that this voice is done sounding, and the DSP chain of this voice patch instance can be turned off. Mute maintenance is what helps you save CPU when a voice isn't actually making noise.
The mute flag is the easiest to see. First, whenever the stripnote outputs a velocity value, the button is fired. Firing the button sends a "0" to the mute message, which send "mute 0" to thispoly~. That is a notice to thispoly~ that mute should be off (that's what "0" means), and that DSP processing of this instance should be started (if it is off). The right outlet of "busy_handler" also sends a value to the "mute" message (and we'll soon see what it does).
The busy flag is a little more difficult. There is no "busy" message - rather, a value of "0" says that the instance is not busy, and non-zero means it is busy. Again, when the button is fired, a "1" is sent to the check box, which echos a 1 to thispoly~ (telling it "I'm busy now...").
The subpatcher busy_handler is a simple mechanism for determining the "busy-ness" of a patch. If you double-click the "p busy_handler" object, the following patcher is seen:
What you see here is a simple test, which does the following:
- average the absolute value of incoming audio (averaged over 4410 samples);
- if the value is less than a really small number, we must be turned off ;
- we do some number manipulation to set a true/false (1/0) value for the question "Is the patch done sounding?";
- We use route to send a bang in the right direction. If the patch is done sounding, the left output sends a bang, and 0 - 1 is sent from the outlets. If the patch is not yet done, 1 - 0 is sent.
What you will notice (and you will see this in virtually all the poly~-based patches I ever do) is that the busy and mute flags are always set together. When busy is on, mute is off; when mute is off, busy is on. If you maintain this pairing, your poly~ voice patches won't ever get you in trouble.
Thats it for the voice patch. Download the example patch
, and take a close-up look at the code. If you don't understand it, spend a little time with this tutorial, Tutorial 21 in the MSP manual and the poly~ page in the the Reference manual. You need to understand this patch - it is the basis of polyphony handling from here out...
Next in the series - the "master patch".