Found while building a JUCE + Tracktion Engine DAW. Measured on 494e91d (develop) and re-checked against develop 18e307f: the code referenced is unchanged. Happy to provide the test fixture.
Where: PluginEffect::createRenderJob (tracktion_ClipEffects.cpp:1430
on develop 18e307f; PitchShiftEffect::createRenderJob at 1205 has the
same shape) constructs its PluginNode with job->processState.sampleRate,
which is 0 at that moment - the job has not run createAndPrepareRenderContext
yet, and when the stage's INPUT is an earlier stage's not-yet-rendered file
the source info reads 0 too. PluginNode's constructor calls
initialisePlugin (sampleRateToUse, ...) → plugin->baseClassInitialise ({0_tp, 0, 512}) unconditionally (tracktion_PluginNode.cpp:48, :304).
What goes wrong: the hosted plugin is initialised at 0 Hz and the render
runs it that way. PluginNode::prepareToPlay then asserts the rate mismatch
(tracktion_PluginNode.cpp:112) and continues. Anything rate-dependent in
the plugin - a delay line, a filter, an oversampler - is silently absent from
the render. Measured with te's own LatencyPlugin set to 20 ms and hosted as a
clip effect: the render is byte-for-byte the input (transient still at
0.250 s) while the same object reports 20.000 ms of latency, because
latencyTimeSeconds * sampleRate rounds to zero samples. A third-party
saturator with oversampling renders as if it had none.
Reproduction: one wave clip; add a PluginEffect wrapping LatencyPlugin
(latency 0.02) to its ClipEffects; let the chain render; compare the render
with the source. Identical. Assertion at PluginNode.cpp:112 in Debug.
Why an app cannot work around it: preparing the plugin at the right rate
before the render does not survive - Plugin::baseClassInitialise re-runs
initialise whenever the rate or block size CHANGED, so the constructor's
0 Hz call undoes any earlier prepare.
Fix: construct the PluginNode with the source file's sample rate (the
job knows its source), or defer the node's construction to
createAndPrepareRenderContext, where processState.sampleRate is set from
the writer.
Found while building a JUCE + Tracktion Engine DAW. Measured on
494e91d(develop) and re-checked against develop18e307f: the code referenced is unchanged. Happy to provide the test fixture.Where:
PluginEffect::createRenderJob(tracktion_ClipEffects.cpp:1430on develop
18e307f;PitchShiftEffect::createRenderJobat 1205 has thesame shape) constructs its
PluginNodewithjob->processState.sampleRate,which is 0 at that moment - the job has not run
createAndPrepareRenderContextyet, and when the stage's INPUT is an earlier stage's not-yet-rendered file
the source info reads 0 too.
PluginNode's constructor callsinitialisePlugin (sampleRateToUse, ...)→plugin->baseClassInitialise ({0_tp, 0, 512})unconditionally (tracktion_PluginNode.cpp:48,:304).What goes wrong: the hosted plugin is initialised at 0 Hz and the render
runs it that way.
PluginNode::prepareToPlaythen asserts the rate mismatch(
tracktion_PluginNode.cpp:112) and continues. Anything rate-dependent inthe plugin - a delay line, a filter, an oversampler - is silently absent from
the render. Measured with te's own
LatencyPluginset to 20 ms and hosted as aclip effect: the render is byte-for-byte the input (transient still at
0.250 s) while the same object reports 20.000 ms of latency, because
latencyTimeSeconds * sampleRaterounds to zero samples. A third-partysaturator with oversampling renders as if it had none.
Reproduction: one wave clip; add a
PluginEffectwrappingLatencyPlugin(latency 0.02) to its
ClipEffects; let the chain render; compare the renderwith the source. Identical. Assertion at
PluginNode.cpp:112in Debug.Why an app cannot work around it: preparing the plugin at the right rate
before the render does not survive -
Plugin::baseClassInitialisere-runsinitialisewhenever the rate or block size CHANGED, so the constructor's0 Hz call undoes any earlier prepare.
Fix: construct the
PluginNodewith the source file's sample rate (thejob knows its source), or defer the node's construction to
createAndPrepareRenderContext, whereprocessState.sampleRateis set fromthe writer.