If your plugin is happily generating audio or midi (without the editor open), and then you open the editor and there’s a large “hiccup” or lag in the production of audio/midi until the editor appears, where would you look for the cause of that? I’m assuming that ideally this should not happen.
If you have hiccups in audio, you look at your audio thread.
So first in your processBlock
and everything called from there.
And if you’re convinced it’s the opening of the editor triggering this, you would have to figure out how that could have an influence on the audio thread.
Thanks, but that’s precisely the question. Yes the editor is doing it. The audio is running fine. I open the editor, it hiccups. processBlock is lean and mean. So I was looking for ideas on figuring out how it was influencing the audio thread.
Memory allocation?
We can collect ideas, what it could be. But eventually the answer is in the code.
In a well designed plugin, the editor doesn’t affct the audio processing.
It sounds to me like a priority inversion, the processBlock has to wait for some low priority stuff to finish.
This is (most likely, cannot prove the absence of bugs) not in the juce codebase.
The usual suspects:
- system calls, especially disk or network IO
- wrtiting to the console / std::cout / DBG
- accessing the system clock can be problematic
- allocations (you mentioned those)
- any kind of lock / CriticalSection
- calling into the editor using
getActiveEditor()
(shouldn’t be used at all) - AsyncUpdater and ChangeBroadcaster is borderline, they post messages which is only ok if it happens very sporadically. Better avoid it (N.B. some AsyncUpdaters can be found in the JUCE code)
Since it only happens when the editor is opening, calling the editor functions from processBlock() directly or indirectly sounds most likely.
If you can share code, we can comment more in detail.
Thanks for the ideas! I’m sure the problem is in my code; I don’t think posting some would be helpful because it’s a very complicated project, and what I’m doing in processBlock() won’t make much sense.
I probably shouldn’t have said it was “lean and mean” because upon further investigation there’s a lot going on there in terms of function calls outside of it.
So I have to find what “bad thing” I may be doing when opening the editor that interferes with it.
I’m curious if you could elaborate on:
wrtiting to the console / std::cout / DBG
So using DBG can cause audio glitches?
accessing the system clock can be problematic
Do you mean Time::getMillisecondCounterHiRes()
?
If so, what is problematic?
calling into the editor using
getActiveEditor()
(shouldn’t be used at all)
Why might that be?
Thanks!
Actually, I see that the docs say for getActiveEditor():
Note that you should only call this method from the message thread
It does seem that a call to this from a function downline in my code that can be called as a result of processBlock() may be the culprit here. I guess the ScopedLock in getActiveEditor() is the issue?
But if you do have to check for the existence of the editor for some reason, from a function that can be called as a result of processBlock(), is there some good way to do it without using this? Store a pointer to your mainComponent, or a flag that says “editor is open”… or something?
I’d go with this solution, and I’d further expand it to: instead of a flag, use a counter.
Put it in your processor and increment/decrement it in the constructor/destructor of your editor.
This way, you always know if there is one (or more*) editor open for that particular processor.
If the counter must be read in your processBlock, as seem the case, obviously it needs to be an std::atomic
because it’s used to communicate between the audio and message thread.
*In theory, there could be multiple editors open for the same processor. I don’t think any DAW does this, but the theory goes that each editor should be like a View/Controller of the processor, that acts as a Model instead and that holds all the data. In such a scenario, turning a knob in one editor should result in the attached parameter to be updated in the processor, which in turn triggers a notification for updating all other editors, so that they all are kept in sync.
Ideally try to avoid that.
My favourite mental model is the processBlock being an anthill, and the editor is a magnifier to observe the processing (and maybe a manipulator, if you need to divertt the ant roads). But don’t stop the ants.
The safest way is the processBlock sets some atomics like @yfede pointed out. So for instance the processBlock() can set a flag “needsRepaint” and the editor picks it up from a timer, resetting it and painting (in that order, so you don’t miss an update, rather paint twice if in doubt).
Or signal data can be stuffed into a FIFO where the editor can always read the last n samples safely.
About the console: only one thread or process can print at a time. If two would print at the same time, you get garbage data at best. Which means, the audio thread might have to wait until another thread or even process finished printing some possibly large data.
The clock is probably the least problematic, but better be aware of. Usually it is implemented nowadays using an atomic counter, so it is safe. But some implementations might use anything, I just overheard a discussion about it but didn’t get into the details myself. For me it is enough to know there are implementations that are not safe to avoid it. But like I said, probably the least problem.
Thanks to both of you for your comments on this. I have implemented this counter idea, and it works great. I seem to have gotten rid of my audio hiccup!