You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Fix processor sample position and latency units with host oversampling - #1211
With 2x host oversampling, a processor prepared at 96 kHz receives a 48 kHz sample position, and a reported 257-sample intrinsic latency is added to the parent graph as 257 samples rather than 129. This affects sample-based timing and graph delay compensation.
The conversion now lives in element::Processor, covering direct implementations (including the CLAP/native-node path) as well as AudioProcessorNode wrappers. Each processor exposes a stable playhead adapter that scales only timeInSamples by its prepared processing factor; seconds, PPQ, BPM and absent position fields are preserved. Nested graphs compose their local factors once per boundary. Multiplication is bounded to avoid signed overflow.
Intrinsic latency is stored atomically in rendering-rate samples and converted to parent-rate samples, rounding up, in Processor::getLatencySamples(). Manual delay compensation and oversampling-filter latency are already in parent-rate units and remain separate. AudioProcessorNode and ScriptNode forward the common playhead and the audio-processor wrapper reports raw plugin latency, including latency-change notifications, without a second conversion.
Validation on Apple Silicon macOS, RelWithDebInfo, JUCE 8.0.13:
The final independent branch passes all 59 CTest suites (3.42 s).
PluginSampleUnitsTests covers wrapped plugins, direct Processor subclasses, 1x/2x/4x/8x and returning to 1x, latency notifications, manual-delay units, cached playhead rebinding/detachment, optional position fields and integer boundaries.
Nested 2x graphs with 1x/2x/4x children verify both direct and wrapped processors receive the composite sample rate/position, while seconds and the source clock remain unchanged.
Applying the new tests to the pre-review PR revision f1e3cf3 produces 22 failures in the direct/nested cases; the original wrapped-plugin case still passes. The new direct/nested cases pass with the revision.
A Lua DSP regression observes the actual script position before/after reload and playhead rebinding. It reproduces four failures with the old ScriptNode forwarding override and passes with the shared clock.
Original regression evidence on upstream d0226686: six assertions failed, including -48000 != -96000 and 257 != 129 at 2x.
The direct-subclass test exercises the shared path used by CLAP; it is not an audition of a third-party CLAP binary. Graph compensation still uses integer samples; no fractional delay lines are introduced. Linux/Windows validation is left to CI. This remains an independent host-only change based on upstream main; no private extensions are included.
The final change also merges without conflicts with #1212–#1214; all 62 CTest suites pass together (3.34 s).
The reason will be displayed to describe this comment to others. Learn more.
I like where this is going @jiri-god, but doing the playhead work in audioprocessornode will only affect VST, VST3, AU, and LV2 plugins. Clap implements element::Processor directly, and all internal nodes (mostly) are element::Processor's as well. The good thing is, all of the latency information you're using already exists in the base class. Should me a relatively simple move of this code up a level to the the Processor class.
jiri-god
changed the title
Fix plugin sample position and latency units with host oversampling
Fix processor sample position and latency units with host oversampling
Oct 30, 2026
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution. You have signed the CLA already but the status is still pending? Let us recheck it.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
With 2x host oversampling, a processor prepared at 96 kHz receives a 48 kHz sample position, and a reported 257-sample intrinsic latency is added to the parent graph as 257 samples rather than 129. This affects sample-based timing and graph delay compensation.
The conversion now lives in
element::Processor, covering direct implementations (including the CLAP/native-node path) as well asAudioProcessorNodewrappers. Each processor exposes a stable playhead adapter that scales onlytimeInSamplesby its prepared processing factor; seconds, PPQ, BPM and absent position fields are preserved. Nested graphs compose their local factors once per boundary. Multiplication is bounded to avoid signed overflow.Intrinsic latency is stored atomically in rendering-rate samples and converted to parent-rate samples, rounding up, in
Processor::getLatencySamples(). Manual delay compensation and oversampling-filter latency are already in parent-rate units and remain separate.AudioProcessorNodeandScriptNodeforward the common playhead and the audio-processor wrapper reports raw plugin latency, including latency-change notifications, without a second conversion.Validation on Apple Silicon macOS, RelWithDebInfo, JUCE 8.0.13:
The final independent branch passes all 59 CTest suites (3.42 s).
PluginSampleUnitsTestscovers wrapped plugins, directProcessorsubclasses, 1x/2x/4x/8x and returning to 1x, latency notifications, manual-delay units, cached playhead rebinding/detachment, optional position fields and integer boundaries.f1e3cf3produces 22 failures in the direct/nested cases; the original wrapped-plugin case still passes. The new direct/nested cases pass with the revision.ScriptNodeforwarding override and passes with the shared clock.d0226686: six assertions failed, including-48000 != -96000and257 != 129at 2x.The direct-subclass test exercises the shared path used by CLAP; it is not an audition of a third-party CLAP binary. Graph compensation still uses integer samples; no fractional delay lines are introduced. Linux/Windows validation is left to CI. This remains an independent host-only change based on upstream main; no private extensions are included.
The final change also merges without conflicts with #1212–#1214; all 62 CTest suites pass together (3.34 s).