Fundamentals
Export Targets
Code Export
Patcher UI
Special Topics
RNBO Raspberry Pi OSCQuery Runner
Working with Polyphony and Voice Control
Manage multiple instances of a patch and automatically route messages to available voices
In the context of audio synthesis, polyphony usually refers to using the same signal processing logic to process multiple streams of audio simultaneously. Think of playing a chord on a digital piano: each finger is playing a different pitch, but the sound synthesis technique is the same for each finger. However, polyphony isn't just for simulating instruments with multiple keys. You might also create a polyphonic audio effect, perhaps slightly de-tuning a single audio stream by different amounts to create a chorus-like effect. This guide will look at how to use RNBO to achieve both.
Simple Polyphony
Making a RNBO patcher polyphonic could not be easier: simply add the @polyphony attribute. The following patcher supports up to 4 simultaneous voices.
Enabling polyphony essentially creates a copy of the RNBO patcher for each polyphonic voice. Adding polyphony in this way also enables the simple
voice controller for the RNBO patch. Voice control refers to how messages are routed to each voice. The simple
voice controller works in pretty much the way you'd expect. When the patcher receives a MIDI note on message, the simple
controller finds the first available voice and sends the MIDI note there. It then marks that voice as busy until it receives a MIDI note off event with the same pitch. In the event that all the voices are busy when a new note on message comes in, the voice controller will "steal" the oldest voice and send the MIDI message there.
Top Level vs. Subpatcher Polyphony
When it comes to polyphony, RNBO gives you a lot of flexibility. The top-level rnbo~ object can be polyphonic, as we've seen, but so can each RNBO subpatcher. Each patcher and subpatcher can have their own level of polyphony, or none at all.
Top-level polyphony works great for quick, simple polyphony, and is especially well suited for MIDI instruments. It has the added advantage that the actual maximum number of polyphonic voices can be re-configured during export, in case you want to support a different number of voices for a particular target.
Subpatcher polyphony is a bit more flexible than top-level polyphony. It's very common to have a RNBO patch with both polyphonic and non-polyphonic components—this is commonly where it's convenient to implement polyphony at the subpatcher level. For example, you might have a polyphonic sampler, where each voice is modulated by a non-polyphonic LFO. In this case, the sampler would be in a polyphonic subpatcher, while the LFO would be in the non-polyphonic, top-level RNBO patch.
Another very common use case for subpatcher polyphony comes up when you want to have more control over voice management. For example, you might want to build a multi-sampler, where playing a single note actually triggers multiple, layered samples. In this case, you will probably want to explore user
voice control.
Subpatchers that utilize polyphony have another advantage, which is that it allows for the use of patcher UI objects such as toggle and meter~. UI objects will not work in a top-level polyphonic RNBO patcher.
User Voice Control
The top-level RNBO patcher always uses the simple voice
controller, which as we've seen routes MIDI note on events to available voices, while muting any unused voices. RNBO subpatchers on the other hand let you specify whether you'd like to use the simple
or the user
voice controller.
With the user
voice controller, the subpatch will no longer do automatic voice management of any kind. That means that by default all events will be routed to all voices, including MIDI events. You can configure this behavior by using set target
, which (just like the "target" message for the poly~ object in Max) will let you route messages to a particular voice.
The most straightforward way to use the user
voice control is simply to leave all voices unmuted. This can be a really handy way to implement a multichannel effect, similar to the mc. family of objects in Max. You can use the voice object to change the behavior of each polyphonic voice depending on its index.
In order to support voice management, the RNBO subpatcher will expose a voicestatus
outlet when user voice control is enabled. This will output a message whenever the mute status of any voice changes. If you want to do your own voice management, you can use this outlet in conjunction with the target
and mute
parameters to route messages to particular voices. Voice management at a very high level looks something like this:
- When the patch receives a MIDI note, determine which voice should receive the note.
- Use the
target
attribute in conjunction with the set object to set the receiving voice on the polyphonic subpatcher. - When the voice finishes processing the note, receive a message from the
voicestatus
outlet. - Register somehow that the voice is now free and ready to receive note events again.
For more details on building your own voice controller with user
voice control and codebox, see Building a Note Controller with Codebox.
Polyphonic Parameters
When you make a RNBO patcher polyphonic, RNBO will duplicate every parameter in the patch, creating a parameter specific to each voice. By default, this parameter is hidden behind a global meta-parameter that controls all per-voice parameters simultaneously.
You can enable the @exposevoiceparams
attribute (@exposevoiceparams 1
) to create an addressable id for every param and param~ in each voice. In Max, this creates an attribute (listed in attrui) for each parameter of each voice. This allows you to configure each voice individually.
If you like, you can also enable per-voice params during target export by enabling the "Expose per Voice Params" option in the export sidebar.. This will create individually addressable parameter IDs for each parameter in the root-level RNBO patcher (but not for parameters in a RNBO subpatch).
For the root-level RNBO patcher, the per-voice parameter ID will look like poly/voice_number/parameter_name
. For RNBO subpatchers the per-voice parameter ID will have the form subpatch_name/voice_number/parameter_name
. So for [p @title sub @polyphony 4 @exposevoiceparams 1]
with a parameter named freq
, the 3rd voice will have the ID sub/3/freq
. Note that if @exposevoiceparams
is enabled in both the rnbo~ and p subpatcher, ALL parameters will have the "poly/" prefix, including those in the subpatches. So the above example path would become poly/sub/3/freq
.
Lastly, you can use the voice object to see which voice was affected whenever a parameter changes.