Learn Working with Polyphony and Voice Control

Getting Started

Welcome to RNBO

Quickstart

RNBO Basics

Key Differences

Why We Made RNBO

Fundamentals

Audio IO

Messages to rnbo~

Using Parameters

MIDI in RNBO

Messages and Ports

Polyphony and Voice Control

Working with Polyphony and Voice Control

Polyphony and Voice Control in Detail

Audio Files in RNBO

Using Buffers

Using the FFT

Export Targets

Export Targets Overview

VST/AudioUnit
Max External Target
Raspberry Pi Target
The Web Export Target
The C++ Source Code Target

Code Export

Working with JavaScript
Working with C++

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.

basic-polyphony.png

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.

polyphony-options.png

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.

working_with_poly

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.

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.

Screen Shot 2022-10-10 at 2.53.00 PM.png

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.

poly-mc-style.png

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.

meta-param.png

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).

working_with_poly_1

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.

Screen Shot 2022-10-15 at 6.29.38 PM.png