Offline render drops beginning of audio when plugin reports latency (PDC)

Hello,

I’m reporting a reproducible issue in WaveLab Pro 12 that has been reported by users of my plugins, and which I’ve since been able to reproduce myself.

Summary

When rendering offline, if a plugin reports non-zero latency, the rendered file is correctly time-aligned, but the beginning of the audio is missing. The amount missing corresponds to the reported latency.

Minimal reproduction

The attached ZIP contains a minimal test plugin (Latency_v100) designed solely to demonstrate the issue.

What the test plugin does:

  • Takes incoming audio

  • Delays it internally by 1000 ms

  • Reports 1000 ms latency to the host

  • Outputs the delayed signal

Results

  • REAPER: offline render is an exact copy of the processed signal

  • WaveLab Pro 12: offline render is time-aligned, but the first 1000 ms of audio are missing

Observation

What seems to happen in WaveLab is that audio processing through the plugin does not begin until the rendered file itself starts. When latency compensation then shifts the signal back, there is no earlier processed audio available, so the beginning is missing.

For PDC to preserve the beginning of the signal, it would seem necessary for the host to process audio before the rendered file start when latency is reported, so that compensated audio exists at time zero.

Other third-party plugins with significant latency appear to render correctly in WaveLab, which suggests there may be a host-specific mechanism or expectation involved when handling latency during offline render.

Attached ZIP

The ZIP includes:

  • Original input audio

  • REAPER render through the test plugin

  • WaveLab render through the same plugin

  • Test plugin binaries:

    • Windows: VST3

    • macOS: VST3 and AU (not yet tested, I did my tests on Windows)

Download ZIP: www.saintpid.se/download/9475/

I hope this helps clarify the issue and makes it easy to reproduce.
Please let me know if you’d like me to test any variations or provide additional information.

Best regards,
Johannes

The vast majority of plugins have latency. Therefore, you can imagine that latency compensation is something that is very well tested in WaveLab!

I had a quick look at your plugin. What I can see is that WaveLab requests the latency to your plugin several times. Your plugin tells the latency is zero, but the last time this is requested (1 sec).
You should try to understand why your plugin returns zero.

Thanks for checking this — that’s very helpful.

If WaveLab is indeed receiving a latency value of zero during the initial queries and only gets the correct value later, that would explain the behavior during offline render.

The same reporting approach appears to work as expected in other hosts, which suggests that WaveLab may have stricter requirements regarding when latency information needs to be available.

I’ll look into why the plugin reports zero latency during the early requests and whether it’s possible to ensure that the correct value is reported consistently from the start.

Thanks again for taking the time to investigate this.

Best regards,
Johannes

SynthEdit plugins can change their latency at runtime, and therefore make use of the VST3 feature:

restartComponent(kLatencyChanged) // This must be called in the UI-Thread context!

Naturally, for the Processor (on the real-time thread) to inform the Controller (on the UI thread) of the latency change, it’s going to take a few ms.

The reason the latency is not known ahead of time is that it can depend on the state of the parameters and also on the sample-rate.
e.g. if the user turns on the oversampling feature extra FIR filters get inserted in the signal path, and the latency will change, or maybe not if the Processor is already running at a sufficiently high sample-rate like 96k (and we can skip the oversampling).
Once the latency is know, the plugin caches it and if the plugin is reset or placed into offline mode, it can report the latency instantly to the DAW.

Most DAWs don’t encounter any problem with this because they either pre-roll the plugins for bar or two before doing any serious processing, or they run the plugins in real-time mode up until when the user chooses to perform an off-line render.
Both these scenarios give the DAW time to cope with kLatencyChanged without glitches.

Wavelab is different; it does not run the Processor at all until it’s time to perform an offline render. And worse - Wavelab instantiates a fresh copy of the Processor object for each and every offline render. Each fresh processor has to recalculate its latency from scratch, and don’t get the usual benefit of being able to cache the latency value like in a ‘normal’ DAW.

I can’t see any obvious easy fix for plugins with dynamic latency under these challenging conditions.

I hope that helps you to understand what is happening.

It’s perfectly legal. There are many plugins that use dynamic latency (restartComponent…). WaveLab is compatible with them.

A fresh copy of the processor object for every render is something perfectly legal and in fact required when you want to render multiple things in parallel. This can be, for example, for the batch processor or most recently when you want to render multiple elements of the audio montage in parallel.

Thanks for confirming that this is legal, I did find that in the documentation also.

But do I understand the documentation correctly?

  1. “A host does not need to instantiate the controller part of a plug-in for processing it.”
  2. To report to the host that the plug-in latency has changed - “The plug-in should call from the editController its component handler restartComponent function with flag kLatencyChanged”

Therefore under these conditions (which is how Wavelab works), it is impossible for the Processor to report a latency change to the host.

If you can confirm that, I can pass on that this issue is a limitation of the VST3 standard, and there is currently no clear fix.

Cheers,

Jeff

Points #1 and #2 are true.

But point #2 is only if the user changes an option in the plugin UI, so that the host can know this change and adapt to this new latency during playback.

But when rendering, the latency is not communicated to the host through restartComponent but through IAudioProcessor::getLatencySamples

This is what your plugin has to do right.

Unlike what you say, there is no limitation of the VST3 standard and there is nothing to fix.

thanks for that information. I appreciate your time!