Why does offline rendering with VST3 plugin show no audible or waveform change? (Tracktion Engine/JUCE host, no UI and plugin UI both tried)

Hi all,

I’m developing a batch audio processor using Tracktion Engine 3.2.0 and JUCE 8.0.7.Problem:When I use offline rendering (Renderer::renderToFile) to process audio with a VST3 plugin (Graillon 3), the exported audio file is almost identical to the input—no matter how I set the plugin parameters (e.g., pitch shift, wet/dry), there is no audible or waveform difference.This happens both when running in console/no-UI mode and when launching with GUI/ScopedJuceInitialiser_GUI initialized (even if the plugin UI is shown/created).

// (1) Init engine and edit
auto edit = initializeEngine(engine, Edit::EditRole::forRendering);

// (2) Load plugin (VST3)
auto plugin = createAndInitializePlugin(engine, *edit, pluginPath, pluginName);

// (3) Set plugin parameters
auto params = plugin->getAutomatableParameters();
params[18]->setParameter(1.0f, NotificationType::sendNotificationSync); // Pitch shift
params[1]->setParameter(1.0f, NotificationType::sendNotificationSync);  // Wet
params[0]->setParameter(0.0f, NotificationType::sendNotificationSync);  // Dry
params[63]->setParameter(1.0f, NotificationType::sendNotificationSync); // FX Enable

// (4) Setup track and wave clip
auto audioTrack = edit->insertNewAudioTrack({}, nullptr, true);
audioTrack->pluginList.insertPlugin(*plugin, 0, nullptr);
auto* clipTrack = dynamic_cast<ClipTrack*>(audioTrack.get());
auto clip = clipTrack->insertWaveClip("offline_clip", audioFile, clipPos, false);

// (5) Offline render
Renderer::Parameters renderParams(*edit);
renderParams.destFile = juce::File(outputPath);
renderParams.usePlugins = true;
renderParams.tracksToDo.setBit(0);
renderParams.allowedClips.add(clip.get());

Renderer::renderToFile("Offline render task", renderParams);

No matter what, the exported buffer is unchanged. I checked:

Plugin definitely loads (log prints OK)

Parameters after setParameter calls print correct values

Input buffer is not silent

What I’m asking
Why is offline rendering (no matter with/without UI) not producing any plugin processing?

Is there a known limitation in Tracktion Engine or JUCE host code that would cause VST3 plugins to “ignore” processing in offline/batch export?

Do I need to set a special host flag, “realtime mode”, or avoid kOffline mode for plugin rendering?

How does Waveform DAW achieve effect export in its “export”/render dialog? (I want to batch export with effects, like in Waveform)

Any advice to get VST3 plugins to process audio during offline rendering, or to “trick” the plugin/host so that effects are included in the output file?

Thanks for any insight!
(If you need to see more code, I can post the full rendering pipeline.)

Do you get the same problem with other VST3 plugins? If not, maybe Graillon 3 has some specific issue like taking a lot of time to initialize before it can process the audio successfully. To test, I would insert a sleep of 1 second or so into the code before calling Renderer::renderToFile. Obviously, even if that works, it’s not the ideal solution. (Unfortunately, these kinds of hacks are sometimes necessary when implementing offline rendering.)

Thanks for your reply!

I tried inserting a sleep of 5 seconds before calling Renderer::renderToFile, but unfortunately it still doesn’t work—the result is the same. I also tested with different plugins (not just Graillon 3), but the problem persists.

What actually happens is that after the render, the console outputs:
Render failed! Output file was not created.
No new .wav file is generated on my disk.

From my understanding, just calling Renderer::renderToFile should be enough to export a processed .wav file, right?
But in my case, it seems the render path is not working at all.
The log message is printed when it detects that the target file doesn’t exist after rendering.

Do you have any suggestions on what might be causing this, or what else I could check to make sure the file is actually written?
Any advice would be greatly appreciated!

Is there any extra parameter or configuration needed to make sure Tracktion Engine writes the rendered file to disk?

I am doing something similar at the moment, albeit for REAPER and with ReaScripts to automate the process of A/B testing features in different versions of our plugins.

One thing that has been important to get right, is that channel bus assignments that route the output of an audio track + plugin → another audio track. In REAPER I have to set the ‘receiving track’ (which is the destination for a “Send”) to “Pre-Fader (post FX)” configuration, in order to receive audio that has been processed by the FX (our plugin in this case).

Perhaps you are running into a similar circumstance vis a vis bus definitions and assignments?

From my understanding, just calling Renderer::renderToFile should be enough to export a processed .wav file, right?

No, not necessarily. What does the Audio Graph look like? If there are no routes of audio out from one track → through FX → another track input .. this would explain your scenario.

Thanks for the suggestions!

Just to clarify my current audio graph structure and workflow in Tracktion Engine:

I create a new AudioTrack and insert it into the Edit (insertNewAudioTrack).

The plugin is inserted directly into the pluginList of this audio track, and the plugin initialization completes without error.

My audio buffer is written to a temporary .wav file, which I then insert as a WaveClip onto the same AudioTrack.

For rendering, I configure Renderer::Parameters so that tracksToDo includes only this single audio track, allowedClips includes only the inserted clip, usePlugins = true, and useMasterPlugins = false.

I call edit->flushState() before rendering to ensure the Edit state is up-to-date, and then call Renderer::renderToFile for offline rendering.

There’s no use of aux sends, busses, multi-track mixing, or master track routing. The structure is a straight line: input clip → track → plugin → render output. There shouldn’t be any audio routing break in this setup.

Despite this (and after checking file permissions, plugin loading, etc.), the render still fails and no output file is created.
I’m currently printing the Edit/Track/Clip/Plugin structure before render to double-check everything is as expected.

If you see anything obviously missing in my approach, or have ideas about what else could cause renderToFile to silently fail in this single-track, single-clip setup, I’d appreciate any further advice!