Polyphony
Max provides a number of systems for managing polyphony. If you've every played an electric piano before, the concept of polyphony should be intuitive. When you press multiple keys at the same time, each key sounds independently, with each note lasting until it fades out, or until each key is released. However it's accomplished, handling polyphony always involves the same basic tasks:
- Routing an input to a specific voice
- Managing the independent state of each voice
- Deciding whether a particular voice is busy
- Muting a voice when it's not in use
ddg.mono
The simplest way to handle polyphony is not to handle polyphony. The ddg.mono object implements a controller for a monophonic synthesizer with polyphonic input. If you were using a keyboard input, when pressing a second key, ddg.mono would shift the pitch to the new key, no longer tracking the first key.
Using poly
The poly object is a polyphonic voice controller for MIDI note-on and note-off events. When it receives a note-on event, it assigns that event to the first available voice. When it gets a note-off event with the same pitch, it marks that voice as no longer in use.
The poly is a voice controller only. That means that it doesn't manage the voices themselves—it won't help you turn one oscillator into several. If you want to have multiple voices sounding at the same time, you should use mutiple patchers, the MC system, or the poly~ object.
Using poly~
The poly~ object combines the voice controller of the poly object with a system for managing multiple voices. When you load an abstraction into poly~, it will create one copy of that abstraction for each voice. Then, poly~ gives you the tools to route incoming messages to a specific voice, while also managing the individual state of each voice.
Viewing voices
Just like other abstractions, you can double-click on a poly~ object to view the patch that it's loaded as an abstraction. However, since poly~ loads one copy of the abstraction for each voice, you'll also see a number next to the name of the patch, indicating which voice you're currently looking at.
You can send poly~ the messge open
to view the loaded abstraction for a particular voice. The messages open 1
will show the first voice, open 2
will show the second voice, and so on.
Event inputs
When poly~ receives a message, it uses one of several systems to determine which voice will receive that message.
The simplest system uses the @target
attribute. When you set the @target
attribute to a particular voice number, all subsequent messages will be routed to that voice. If you set @target 1
, then all messages will be routed to the first voice. Set @target 0
to make all voices the target, in which case each messages will be copied and sent to each voice.
Messages starting with the symbol note
will be automatically routed to the first available voice. You can tell poly~ when a voice is busy by using the thispoly~ object—see busy state for more details.
The poly~ object can also handle MIDI note-on and note-off events using the midinote
message. When you send poly~ a message like midinote 60 127
, it will get a free voice and send that note-on event to that voice. When you send poly~ a note-off event like midinote 60 0
, it will find the voice associated with the pitch 60
and send the note off event to that voice. The poly~ object can also receive messages with the symbol midievent
, like those coming from the rightmost outlet of the midiparse object. In this case, the system for assigning note-on and note-off events to voices is the same.
Finally, poly~ is also an MPE-compatible object. Use mpeparse and mpeformat to generate mpeevent
messages, which poly~ will then route to the appropriate voice. The really cool thing about this approach is that it can manage not only note-on and note-off events, but also polyphonic aftertouch, pitch bend, and other MPE events.
Busy state
In order for poly~ to route note
and midinote
messages to the right place, it needs to know when a note is busy. From within a particular poly~ abstraction, use the thispoly~ object to manage busy state. Send thispoly~ the message 1
to mark the note as busy, and the message 0
to indicate that the voice is free, and ready to recieve new note
or note-on messages.
Muting
When a poly~ voice is muted, Max will skip signal processing for any signal-rate objects in that particular voice. Objects can still pass messages to each other, but no signals will flow between objects. Muting makes poly~ more efficient, since it won't waste processing power computing silent signals for unused voices. This can be especially important when you're working with multiple poly~ objects, or in the context of a custom Max for Live synthesizer.
Inlets and outlets
Unlike regular abstractions, poly~ uses the special objects in and out in order to declare inlets and outlets, and the objects in~ and out~ to declare signal inlets and outlets.
Unlike the inlet and outlet objects, these objects take an argument to specify their index. This has some upside; for example, you can reference the same inlet/outlet in multiple places, and you can place the inlet/outlet objects in any left-to-right order that you like.
There's also a special object polymidiin, specific to poly~. This object receives any messages sent to poly~ with the midievent
symbol, like the ones that come from the rightmost outlet of mpeparse and midiformat.
Parameters
The poly~ object supports the use of the param object, which lets you define custom attributes on your poly~ object. You can control these with an attrui object, just like a regular object attribute. You can set a @min
and @max
value on your param too, constraining it to a given range. See the param help file for more information.
mc.poly~ and mcs.poly~
The poly~ object has two wrappers, mc.poly~ and mcs.poly~. These objects help to bridge the gap between the mc system and the poly~ object. The mc.poly~ object is maybe the simplest to understand: it turns each signal input to poly~ into a multichannel input, where the number of channels is the same as the number of polyphonic voices. Unlike regular poly~, the mc.poly~ object does not add together the signal output from each voice, but keeps each channel separate.
The mcs.poly~ object collapses all of the signal inputs and outputs of a poly~ object down to a single multichannel input and output. The number of input channels and the number of output channels depends on the number of in~ and out~ objects with different indices in the poly~ abstraction. Each audio input and output will be copied and sent to each polyphonic voice.
Using MC Objects
The MC wrapper can manage polyphony without the use of the poly~ object. The mc.noteallocator~ object works of of MIDI note-on and note-off events, similar to how poly~ responds to midinote
and midievent
messages. There's also the mc.voiceallocator~ object, which works more like poly~ in response to the note
message. For more information, see the guide on MC Wrapper Polyphony.