How to reset plugin buffers in hosts that suspend ProcessReplacing()?

DSP, Plugin and Host development discussion.
RELATED
PRODUCTS

Post

I have found that some hosts, like Wavelab, completely suspend processing when the transport is stopped (it doesn't stream zeroes, it stops calling ProcessReplacing()). It also does not call "Reset()" on transport start or stop either.

So, if you need to clear plugin buffers on transport stop/start in a host like this how do you do it? I know it CAN be done because I have tested other plugins that DO reset on start/stop in hosts like this.


(BTW - my plugin is a compressor. If I don't flush it's buffers on stop/start it will apply whatever gain reduction it had when last stopped to whatever new section the cursor is moved to causing a fade-in. If the compressor's Release time is set slow this can take many samples. Not good!)

Post

Fender19 wrote: Tue Jan 08, 2019 7:38 pm I know it CAN be done because I have tested other plugins that DO reset on start/stop in hosts like this.
How do you know the other plugins stop/start? Are they some obvious cases like delays with lots of feedback or reverbs with long tails?

Post

Xenakios wrote: Tue Jan 08, 2019 7:47 pm
Fender19 wrote: Tue Jan 08, 2019 7:38 pm I know it CAN be done because I have tested other plugins that DO reset on start/stop in hosts like this.
How do you know the other plugins stop/start? Are they some obvious cases like delays with lots of feedback or reverbs with long tails?
Yes, similar plugins to mine. They are compressors and they reset immediately to zero attenuation either on transport stop or start.

MCompressor from Melda Production freezes when transport Stop is pressed but then instantly resets to 0dB gain reduction when Play is pressed.
Compressor from Dead Duck software resets to zero gain the instant Stop is pressed.

MY plugin, on the other hand, freezes when play is stopped and then resumes from whatever status it had when Play is restarted. It doesn't resume in a fresh state even though I have a Reset() block in my code.

Post

If there is no API support for this and if you still have to do it, you can use QueryPerformanceCounter on Windows. If it detects a jump in the returned timestamp then the host has stopped calling ProcessReplacing for a while.
~stratum~

Post

stratum wrote: Tue Jan 08, 2019 8:17 pm If there is no API support for this and if you still have to do it, you can use QueryPerformanceCounter on Windows. If it detects a jump in the returned timestamp then the host has stopped calling ProcessReplacing for a while.
Thank you for the info but that's seems kinda "ugly" (i.e., over my head) and platform-specific.

Is there no simple means for plugins to be notified of transport play/stop?

I tried using "TransportIsPlaying" from "GetTimeInfo" but the problem I ran into with that approach is it only works in hosts that continually call ProcessReplacing() while stopped. It doesn't work in hosts like Wavelab that suspend all processing on Stop. (Maybe there is some other place to put this code that IS called always?)

What's confounding is, as I said in the OP, I have plugins that DO reset, both their buffers and GUIs, in Wavelab on stop/restart. How are they doing it?

Post

What API/Library do you use? IPlug?

(It won't be very helpful but I don't recall any weird behavior in WaveLab in this regard - though my experience it limited to (pure) VST2 and most of tests went too many years ago.)

What you describe is definitely a weird behavior (taking our prev. discussion into account): a host may not request any reset if it keeps calling the processing function regardless of the start/stop state, but it definitely should trigger at least one of the `prepare` functions after processing calls are really suspended - otherwise it's all gets quite screwed.

Now counting that VST API itself has no explicit `reset` function but a set of (sometimes confusing, at least in v2) `here we go` functions, it may be just a sort of gap between what the wrapping API expects from a host and what host actually does (and indeed WaveLab seems to expose quite unusual legacy behaviour, e.g.: viewtopic.php?p=969582#p969582).
A quick search brings me to: https://forum.cockos.com/showthread.php?t=130351

(a pure guesswork: WaveLab probably uses `suspend/resume` on stop/start and these functions seem to not trigger the `Reset` in IPlug (?)).
Last edited by Max M. on Wed Jan 09, 2019 1:49 am, edited 1 time in total.

Post

Max M. wrote: Wed Jan 09, 2019 1:21 am What API/Library do you use? IPlug?
Yes, IPlug. Access to DAW transport functions seem limited but maybe that's because IPlug only supports what is common to all formats (AU, AAX, etc.).
Max M. wrote: Wed Jan 09, 2019 1:21 am(a pure guesswork: WaveLab probably uses `suspend/resume` on stop/start and these functions seem to not trigger the `Reset` in IPlug (?)).
Could be. If that's the case then this is a problem/deficiency in IPlug. Is there a way I can add that trigger to the Reset() method?

BTW - I find it ironic that Steinberg apps seem to be the LEAST compliant DAWs with the VST spec. What works in other DAWs does not always work in Wavelab, Cubase, etc. Any idea why that is?

Post

Is there a way I can add that trigger to the Reset() method?

No idea, sorry. But first of all I'd ensure this is the case (for example using something like http://www.hermannseib.com/pluginconsultant/).

What works in other DAWs does not always work in Wavelab, Cubase, etc. Any idea why that is?

WaveLab has always-like been that sort of "semi-abandoned" apps where they only added new bells and whistles but almost never touched its audio engine, so it still(?) works almost just like it did in 1999 :). While each new version of Cubase (at ~2000-2009) was rewritten almost from scratch and since then it's also kept to be continuously updated in all aspects.
(Honestly I did not recall any really major differences between Cubase (at least starting from v5) and other similar DAWs - but I guess our expectations of differences mostly depends on what DAW you get to use first - for example for me it was actually the Repear I find quite unusual in certain ways of its VST handling :·)
Btw., now it's even more ironic to recall that VST SDK (v1) itself was originally based on WaveLab SDK (of that time) - the SDK had pretty much same set of functions (lacking just a few goodies for a real-time support) so it went almost like only names changed from `wl` to `vst`. So technically... :hihi:

Post

An ignorant comment for what it is worth-- Might a hint be gleaned from VstTimeInfo.samplePos? Or maybe some related strategy?

This may be a cando that some hosts support and some do not. So maybe it could be used as another clue a plugin could use to guess whether to do a reset?

So far as I recall timeinfo is most likely valid called from process replacing. I remember my hosting would return samplePos

For instance if the host seems to be returning basically accurate samplePos info, and the plugin notices a sudden large change in samplePos, it might be a good hint that play location has jumped and maybe decide to do a reset?

Post

JCJR wrote: Wed Jan 09, 2019 7:38 am An ignorant comment for what it is worth-- Might a hint be gleaned from VstTimeInfo.samplePos? Or maybe some related strategy?

So far as I recall timeinfo is most likely valid called from process replacing. I remember my hosting would return samplePos

For instance if the host seems to be returning basically accurate samplePos info, and the plugin notices a sudden large change in samplePos, it might be a good hint that play location has jumped and maybe decide to do a reset?
Thank you.

I have tried doing that but was unsuccessful. The problem is that Wavelab suspends calls to ProcessReplacing() when the transport is stopped. So you can't put any code in there to check transportIsPlaying, for example, because it would only run when - drum roll - the transport is playing! (catch 22)

I also considered if there was a way to save the current sample position and compare it to some previous sample position - but that doesn't work either. If the DAW is running the sample position is always changing. If the cursor is moved the sample position changes too - so this doesn't work either! You would have to be able to capture the sample position the instant playback stops - but when playback stops ProcessReplacing() does too so you can't have code there to capture the sample position! (another catch 22)

But, I do agree it seems possible to use sample position SOMEHOW and will keep thinking about that...

Ultimately, it seems there needs to be a stop/start signal from the host to the plugin - which doesn't seem to exist. So it's a total mystery to me how other developers flush their plugins in hosts that don't call Reset() on start/stop. And these plugins are cross platform too!

Let me ask this - how do plugins clear reverb tails on hosts stop/start? If you have a 10 second reverb for example, it would be a terrible mess for the user to have this tail play when skipping around with the cursor. The plugins I've tried do NOT do this - so, again, somehow there IS a way to reset the plugin even when Reset() is not called by the host.

Post

Hi Fender19. As said earlier, I'm out of date on it and I remember imperfectly. Just wild guessing.

There are other items in the VSTTimeInfo possibly as useful or more useful. I was guessing samplePos could possibly be more-likely "generally supported" compared to some of the tempo-related calls. Perhaps stereo editors and such may not care about tempo or have such data available to report to a plugin.

OTOH, POSSIBLY a host that shuts down ProcessReplacing() when not in playback state, MIGHT be more likely to supply realistic samplePos info? A host that calls resume() then keeps calling ProcessReplacing() "forever after" regardless of play state-- After an hour or two of continuous operation the samplePos datum might be somewhat meaningless? Wheras if the host only runs the plugin during playback, then maybe it would make sense to use samplePos to indicate the current location in the song?

Brief searching last night, I found one complaint about a host which would only update samplePos on every other ProcessReplacing(). IOW, maybe if block size is 256 sample frames, then samplePos would stay the same for two ProcessReplacing(), then it would increment by 512 sample frames on the next ProcessReplacing(). Or something like that.

So there would probably need to be some minor sanity checking to make sure the host is returning "reasonable seeming" data before trying to use samplePos. Even if samplePos is not updated every time then it might still be useful so long as it is accurate when it gets updated.

I was thinking something like, the first thing ya do in ProcessReplacing is to get the samplePos. Maybe ignore a fairly large time-slop range, maybe even as much as 1 second slop. Possible pseudocode though something smarter could probably be thunk up--

CurPos = GetCurrentSamplePos();
if ((LastPos + SampleRate) < CurPos) //time jumped more than 1 sec forward
or ((LastPos - SampleRate) > CurPos) //time jumped more than 1 sec backward
then DoReset()
LastPos = CurPos;
//continue with usual ProcessReplacing code

Thataway you wouldn't reset right after the host suspends ProcessReplacing(). You would notice that you had relocated in the song and then do a reset at the beginning of the first ProcessReplacing() that you get called after the host has resumed playback state at a significantly changed location.

Of course a reset in that location should run fast and not churn memory. Ought to be safe to zero a buffer or init buffer pointers or whatever, just not resize or alloc new buffers.

Post

Steinberg should fix it.

Post

chipnix wrote: Wed Jan 09, 2019 10:05 pm Steinberg should fix it.
I'm not too quick to blame Steinberg for this because other plugin developers have figured out ways to make it work regardless.

A host message for this WOULD be great and ideal - but I guess what I'm trying to figure out now is what are those "other ways"?
Last edited by Fender19 on Wed Jan 09, 2019 10:36 pm, edited 2 times in total.

Post

JCJR wrote: Wed Jan 09, 2019 8:46 pm
CurPos = GetCurrentSamplePos();
if ((LastPos + SampleRate) < CurPos) //time jumped more than 1 sec forward
or ((LastPos - SampleRate) > CurPos) //time jumped more than 1 sec backward
then DoReset()
LastPos = CurPos;
//continue with usual ProcessReplacing code
I tried something nearly identical HOWEVER I did not give it a range as you did here (and that's key). We could also take "nSampleFrames" into account to allow for normal contiguous playback from one block to the next without triggering a reset. Incrementing CurPos each time through to Process() loop could also be used to capture the exact spot where samplePos was when playback stopped, but I don't know if that's necessary or practical.

All ideas building on what you "thunk up" above. A little more thought might produce a workable method. Thank you for helping brainstorm! :)

Of course, all of this assumes (as you said) that GetSamplePos() is reported correctly by the host... :roll:

Post

Fender19 wrote: Tue Jan 08, 2019 8:06 pm MCompressor from Melda Production freezes when transport Stop is pressed but then instantly resets to 0dB gain reduction when Play is pressed.
Compressor from Dead Duck software resets to zero gain the instant Stop is pressed.
Already tried asking the devs how they do it? :)
Maybe Voitec(?) (Melda) is too busy for such stuff but the Dead Duck guy is active on KVR too iirc.

Post Reply

Return to “DSP and Plugin Development”