We’re keeping track of the sample position of audio being processed, and detecting jumps or loops by comparing the current timeInSamples with the previous timeInSamples plus the previous buffer size. The assumption is that during normal processing, the previous position plus the previous buffer size will always equal the new sample position.
However, in Cubase 10.5, what I’m seeing is something different. For example, the first time I check the timing info, I see timeInSamples == 0, and the buffer size is 512. But the next time processBlock() is called, timeInSamples is not 512, but some other value (seemingly random, but under 512). This causes my code to assume that a jump or loop has occurred, so it stops what it was doing and resets things internally, which it is not supposed to do until the transport stops or jumps or loops.
Anyone else seeing this? Should I contact Steinberg/Yamaha, rather than asking here?
I think I found the problem. This is the code at the start of getCurrentPosition:
bool getCurrentPosition (CurrentPositionInfo& info) override
info.timeInSamples = jmax ((juce::int64) 0, processContext.projectTimeSamples);
That line makes the timeInSamples always 0 or positive! But there are hosts that allow negative time to exist. Changing a negative time to 0 makes the buffer appear to start at time 0, when in fact it is starting at some negative time. This causes our start time to be 0, even if it should have been a negative value, and so adding the buffer size to 0 gives us a different value for the start of the next buffer than adding the buffer size to the actual starting value for that first buffer!
Why is this being done? Why is negative time not being allowed in JUCE, when it certainly is allowed in a number of hosts (Cubase, Pro Tools, and Logic to name a few). If I modify that code, will something else in the VST3 wrapper break?
Looks like I can modify the wrapper by adding this, if I need to:
/** The time actually provided by the host. May be negative, as opposed to timeInSamples */
info.actualTimeInSamples = processContext.projectTimeSamples;
Defining and setting this new member will allow me to use that value instead of timeInSamples for the comparison. But I dislike having to modify the wrapper. Other wrappers don’t do this. Why does the VST3 wrapper do so?
This is particularly weird. I had seen negative
timeInSamples in Cubase and VST2, and had to account for it, I’m now worried that my VST3 code won’t behave properly due to this arbitrary choice.
Still open in vanilla juce. But was very useful for us.
(btw: we use it only if its flagged as valid of course. eg. AU/AAX don’t use/support it as some VST hosts).
I would like to add that negative time isn’t actually an exotic artifact or edge case. It regularly occurs where a pre-roll is needed, e.g. for recording or synchronization with other programs.
Support for negative time should be the norm, not the exception.
Anyone here know why the VST3 wrapper (only) sets negative times to 0? Or why it can/should not be changed?
In case anyone wondering there is a way to get negative sample position provided by host without hacking the wrapper. Just use PPQ value with the following code
if (forceUsingPPQ && positionInfo->getPpqPosition() != 0.)
playhead.sample = (int64)(*positionInfo->getPpqPosition() * playhead.samplesPerQuarterNote + .5);
playhead.sample = *positionInfo->getTimeInSamples();
forceUsingPPQ is a flag we set for AUv3 targets and VST3+Cubase (speaking up, Cubasis also feeds negative position values to plugins).
But I do agree with the @ans arguments and I’m not sure if this will work in all hosts. So it would be nice to fix VST3 wrapper one day and pass untouched correct sample position from host.
Another dev finds the issue… another dev hacks a patch… and the mysterious ever remaining bug survives another day… it’s said to be as old as the vst wrappers themselves… I’ll never know for once I hath hacked my patch I shall never set eyes upon the unholy code again
I just ran into that problem as well, due to a “behavior” (bug?) in Reaper/VST3:
During preroll, Reaper keeps setting the PPQ position to 0 until we get to the timeline “0”.
However, it does let the plugin know its in a negative samples position, which would greatly help me fix issues with our sequencer.
JUCE seems to filter that negative information:
info.setTimeInSamples (jmax ((juce::int64) 0, processContext.projectTimeSamples));
Anyway to still get that raw information somehow from within the plugin code?
Would love to be able to solve that issue.