Can you know your plugin is being deleted, in order to take some action?

Let’s say you have a MIDI generating plugin sending out note-ons and note-offs from processBlock().

Let’s say you have a “free-running” mode where the host (DAW) does not need to be playing, but your plugin can receive live midi input and generate arpeggios and riffs etc.

If the plugin is playing and generating MIDI that is being routed to other plugins to produce sound, and you delete it, of course some note-offs may not be generated for note-ons that have already gone out. And so you will get hanging notes.

I am of course storing the note-ons and note-offs so I can “stop” and generate note-offs for any currently sounding note-ons.

But how to do this before the plugin is deleted? There seems to be no way to know that you’re about to be deleted so you can take any steps like this. Is this just impossible to do anything about? If the user removes the plugin while it’s generating MIDI, it’s just his problem?

I think so, yes.

1 Like

“A common mistake that people make when trying to design something completely foolproof is to underestimate the ingenuity of complete fools.”

3 Likes

Haha, yes. :smile:
Still, I can’t help but wish I could do something about it…

1 Like

The way I heard the quote is;

“Its hard to make things fool proof because fools are so clever!”

or maybe it was…

“Its hard to make things idiot proof because idiots are so clever!”

2 Likes

You can lock, and avoid being deleted until it’s a sent (or sent an all notes off message).
But that is not the way to go imho.
First of all, most (if not all hosts) have a panic button for this event (hanging notes).
Second of all, why generate notes when the host is not playing?
Some hosts will not even run your process block when the host is not playing.

How would you go about doing that?

Well, it’s an arpeggitor/phrase generator, so you can play live chords into it from an input (i.e. a keyboard) and hear what it will generate from those chords, and decide what kind of input you want to record onto a track by playing with it, without having to “play” the host track. It’s useful to be able to work with it before you want to commit to recording an input track.

As I’m still in development, I haven’t tried a lot of DAWs. I think it works with Reaper, Logic, Digital Performer, JUCE AudioPluginHost… Do you know of DAWs offhand where the processBlock is not sent unless the host is playing? And anyway, in that case, if it doesn’t work, it doesn’t work. But it’s still useful in the DAWs that support it.

You could override the processor’s destructor, but I think it’s better to override the processor’s reset method.
According to juce docs:

A plugin can override this to be told when it should reset any playing voices. The default implementation does nothing, but a host may call this to tell the plugin that it should stop any tails or sounds that have been left running.

At that point you could atomically signal to stop all playing notes and wait for that. Not a neat option, but the only way to do anything before deletion as far as I know.

That is a good reason.

Well, MIDI output only plugins tend to have some sketchy support in hosts. Just beware to mark it as a synth/generator and possibly setting tail length to a very long value. Been a while since I’ve dabbled with these type of plugins, but IIRC there where occasions where Pro Tools and/or Logic would suspend processing in some cases.

1 Like

Yeah, Pro Tools will stop calling processBlock after a short period after stopping the transport (assuming you have ever started it), unless you set a really long tail time. I forget about Logic, (but I seem to recall ARA was different from non-ARA). And Cubase has a checkbox, which, if checked, will suspend processing if there is no audio.

1 Like

Another idea here (if you find user’s are using synths that generally support it) is to send a MIDI “All Notes Off” command instead, since it might be easier to send it out than a bunch of note off messages if you want to minimize your contribution to how much the DAW hangs when it closes in this situation. You might also be able to get away with a shorter tail, for the same reason.

That sounds really strange… So if I want to record my guitar with a distortion plug-in, I will first have to record it, rewind, start play and first then I will hear how it will actually sound?! And I have to do this all over again every time I turn a knob to know the result?

Looks like some people here have forgot DAWs usually have a “monitor live input” feature that allows plugins to run even when the transport isn’t running. :man_shrugging:

2 Likes

Thanks for the ideas! This thing about a “tail” is new to me, as I don’t process audio (although my plugin is an “instrument”).

I’ve tried sending out note-offs or all note-offs in the Processor destructor, but they don’t go anywhere. This concept of a “tail” may be the missing piece - if it causes the plugin to hang around long enough to send out the note-offs.

I see there is a method AudioProcessor::getTailLengthSeconds(), but where do you specify or set a tail length?

EDIT: nevermind, I see you provide this by overriding getTailLengthSeconds, which I had set to “0” - Will experiment with this.

EDIT (again): I see i have no idea how to use this. I specified “5” as the return value for getTailLengthSeconds(), but this is never called by anything.

I tried implementing reset() which gets called prior to being deleted, but sending out anything here doesn’t seem to go anywhere and the plugin is still deleted instantly.

The destructor is the very last method to be called. Once it entered the destructor, no other method can be called without a crash (all members are invalid now). So when you try to send note offs, they still won’t be able to be delivered, because for that to happen processBlock would still need to run.

The host might call this to investigate the behaviour of your plugin. But since it is not processing audio, the host might not bother to call this.

I wonder, most hosts stop processing, when a plugin is added or removed. Only a few are designed to continue running in such a situation. most notably Ableton Live.

So may I ask on which host you get stuck notes, or is this a theoretical investigation?

Yes, again, the noteOff won’t be delivered once releaseResources() has been called. It would violate the api to call processBlock() after calling releaseResources().
Which reset() did you try?

Well, this is with JUCE AudioPluginHost. If I’m sending MIDI out to an instance of the Internal SineWaveSynth, and I delete my plugin, I get hung notes. Perhaps this is not a valid representation of the operation of most DAWs?

AudioProcessor::reset()

virtual void reset ()
A plugin can override this to be told when it should reset any playing voices.

This is called by juce::VST3PluginInstance::deactivate(), prior to releaseResources() and the destructor. But it doesn’t seem to have any effect sending any note-offs from there.

I’m not really sure what you mean by “sending note offs from reset”, as the only way to output MIDI data to the host is from processBlock. If the host doesn’t call processBlock again, there is simply nothing your plugin can do.

Aye. Daniel is right.
Sadly there is no way at that point to signal the audio thread.
processBlock will not be called again.
I see no other option.

I mean “calling code that puts note-offs into a midiOutputBuffer” that would normally be sent by the next processBlock().

But thanks for the idea. I just tried seeing if there was a processBlock() called after the reset(), and it is not. It seems impossible…

I’m not sure what the purpose of reset() is supposed to be, if there are no more processBlocks() called after it.

My understanding is that reset() is meant to clear the internal state of the processor.

Incidentally, I just tried this in Reaper (macOS), and it absolutely will let you delete the plugin while it is generating MIDI, resulting in hung notes. I had hoped to do something about this, but it seems not to be. :roll_eyes:

EDIT: anyway, Reaper has a “send all notes off to all midi outputs/plugins” action, and I’m sure most DAWs do as well, so I guess it’s the user’s problem.