Skip to content

vst3: maxSamplesPerBlock is a maximum, not a fixed block size - #567

Merged
defiantnerd merged 1 commit into
free-audio:nextfrom
defiantnerd:fix/vst3-block-size-is-a-maximum
Sep 15, 2026
Merged

defiantnerd merged 1 commit into
free-audio:nextfrom
defiantnerd:fix/vst3-block-size-is-a-maximum

Conversation

@defiantnerd

@defiantnerd defiantnerd commented Sep 15, 2026

Copy link
Copy Markdown
Collaborator

setupProcessing passed newSetup.maxSamplesPerBlock as both bounds of setBlockSizes. In CLAP, min_frames_count == max_frames_count means every process() call carries exactly that many frames; in VST3, maxSamplesPerBlock is only an upper bound and ProcessData::numSamples may be anything up to it. ProcessAdapter::process() passes numSamples straight through as frames_count.

A plugin with a fixed internal render quantum uses that declaration to skip its input FIFO and run with zero added latency. Given a host that declares 1024 and then renders monitored tracks on 32 — Cubase/Nuendo with ASIO Guard, and anticipative-FX schemes elsewhere — it renders a whole quantum for a call carrying less than one, reading and writing past both the input and the output buffers the host owns.

Found from a user report of a wrapped guitar-amp plugin sounding wrong at 32 and 64 sample buffers while 128 and 256 were fine: those two are whole multiples of its 128-frame quantum, so they happened to be served correctly.

The minimum is declared as 32, clamped to maxSamplesPerBlock. VST3 states no minimum at all, so this is a practical floor rather than a guaranteed one — the smallest block hosts actually render — and the AUv2 wrapper makes the same kind of call with 16. What fixes the bug is that min != max, which no longer lets a plugin infer a fixed block size.

VST3 was the only wrapper declaring a fixed size: AUv2 passes 16, max, AUv3 and the standalone pass 1, max, AAX passes two different constants.

Passing it as min_frames_count too tells a CLAP plugin every process()
call carries exactly that many frames. Hosts that declare a large block
and render on a smaller one (Cubase ASIO Guard, anticipative FX) then
make a plugin with a fixed render quantum read and write past the
buffers it was given.
@defiantnerd
defiantnerd force-pushed the fix/vst3-block-size-is-a-maximum branch from d184fdf to db6c3c8 Compare September 15, 2026 09:34
@defiantnerd

Copy link
Copy Markdown
Collaborator Author

Updated: the declared minimum is 32 rather than 1, clamped so it can never exceed maxSamplesPerBlock.

VST3 specifies no minimum, so neither value is something the spec backs — 32 is the smallest block hosts actually render. The property that fixes the bug is min != max either way; 32 just states the practical floor instead of a value no host produces, and lines up with the AUv2 wrapper declaring 16.

@defiantnerd
defiantnerd merged commit 723bef5 into free-audio:next Sep 15, 2026
28 checks passed
@defiantnerd
defiantnerd deleted the fix/vst3-block-size-is-a-maximum branch September 15, 2026 09:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant