Skip to content

Voices

A dsp instrument describes what one note sounds like. render takes one note’s pitch, velocity and gate, and returns one sample. Playing a chord, or a note that rings into the next one, needs several of those at once — and that is what voices are.

An instrument has 16 voices. Each is a complete, independent copy of the instrument’s running state: its own voice state, its own buffers. When a note starts, it gets a voice; render runs once per sample for every voice that is sounding, and the voices are summed into the track.

So the code you write never deals with polyphony. phase in a sine oscillator is simply the phase; that there are sixteen of them, one per voice, is the engine’s business. This is the same split as in a hardware polysynth, where every voice card holds the same circuit.

A note-on sets a voice’s state back to the initial values in the declarations, and clears its buffers. A new note therefore starts from a known point — an oscillator at phase 0, an envelope at 0 — whatever the voice played before. Without that, a note’s attack would depend on which note happened to use the voice last.

In the current version this applies to plain state in an instrument as well: it behaves like voice state. State shared by all voices — for a global LFO or a cross-voice effect — is meant to come later, and a plain state is where it will go. Use voice state for anything that belongs to one note.

A param is a knob on the instrument, not on a note, so every voice reads the same value. Turning the cutoff moves it for every note that is sounding, as on any synth.

After note-off, gate drops to 0, but the voice is not freed yet: render keeps running so the envelope you wrote can fade the note out. The voice is freed when its output has stayed below −120 dBFS for 64 samples in a row. That is why a release tail rings as long as your envelope makes it, with no release time to declare.

A new note takes the first free voice. When all 16 are busy, the engine steals the quietest one — the voice whose loss is least audible, usually a tail that has almost faded.

note is a MIDI note number, but it is an ordinary number, not a key index: it can be fractional. __native("mtof", 61.5) is a quarter-tone above C♯4. A scale with fractional steps reaches render exactly, with no rounding to semitones on the way.