Skip to content

audiosampler: raise the voice ceiling from 64 to 128 - #289

Merged
bdbarnett merged 1 commit into
mainfrom
sampler-voices-128
Oct 20, 2026
Merged

bdbarnett merged 1 commit into
mainfrom
sampler-voices-128

Conversation

@bdbarnett

Copy link
Copy Markdown
Contributor

audiosampler.Sampler now takes up to 128 voices (audiosampler.MAX_VOICES, was 64). voice_count is still what you ask for, so a board that can't render 128 asks for fewer and pays nothing for the headroom.

Why. Two voice-heavy General MIDI files played through the SoundFont instrument (GeneralUser GS, the pool shared by all sixteen parts, a reserve of 8): a symphony movement of 5 min 17 s and a film-score arrangement of 67 s.

at 64 at 128
film score: keys held at once 67 asked, 3 held notes cut none cut
film score: released tails stopped early 5,918 (2,625 still above about -30 dBFS) 3,317 (395)
symphony movement: keys held at once 31 31
symphony movement: released tails stopped early 665 (417 above about -40 dBFS) 0

Counting release tails, the symphony movement peaks at 90 voices sounding and the film score at over 500 (the bank's release stages are long). 256 voices all but removes the film score's audible truncations, but 128 is the step this change takes.

Cost. One 1,024-frame period (21.3 ms at 48 kHz), every voice looping, panned and pitched apart, x86-64, one core:

64 sounding 128 sounding
CPython 0.27 ms (79x real time) 0.55 ms (39x)
MicroPython unix 0.67 ms (32x) 1.27 ms (17x)

64 voices sounding in a table of 128 cost the same as in a table of 64 (0.28 and 0.28 ms on CPython, 0.67 and 0.67 ms on MicroPython). Whole-piece renders at 128 never came near a period's budget.

Correct. With the pool at 64 voices, both pieces render byte for byte the same on the old and new builds. At 128 the symphony movement, which never needs a steal, is byte for byte the render of a pool of 512. Where a 64-voice pool does steal, its render matches a 128-voice one until the block that takes the first steal and diverges there, not before. A high-pass click count against the 512-voice render finds no clicks at either size.

New tests: 128 voices build and sum (and one more is refused), the first 64 voices of a table of 128 render what a table of 64 does, a steal on the last voice fades the old note out, and 128 voices render inside the block. With the define set back to 64 the four fail. The MicroPython unix build and the three-target sampler probe agree at 64 and 128.

Boards. On a P4, 64 voices are about 2.4 ms of a 5.3 ms block, so 128 sounding at once would not fit there; the limit is a ceiling, not a default, and a board keeps its own voice_count. Not measured on a board here.

A dense General MIDI piece holds more than 60 keys down at once, and the
tails of the notes just let go push the voices sounding higher still. At
64 the sampler had to cut notes still held, and shorten many tails that
were still audible; at 128 neither piece in the check needed a held note
cut. The ceiling costs nothing until a voice sounds: the table is sized to
the voice_count asked for, and an idle voice is skipped.

Tests: 128 voices build and sum, the first 64 of a table of 128 render what
a table of 64 does, a steal on the last voice fades the old note, and 128
voices render inside the block. The roadmap bullet is done.
@bdbarnett
bdbarnett merged commit c03a357 into main Oct 20, 2026
80 of 85 checks passed
@bdbarnett
bdbarnett deleted the sampler-voices-128 branch October 20, 2026 18:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant