I am curious if using the following code would break the audio thread.
I am currently using locking so functions don’t get called before they are completed. However, I was wondering if it was possible to sneak in a loadPreset call in between processBlocks without the need or hope that suspending the buffer, by simply adding a mutex.
I’m using custom buffers so suspend is not an option, I am using atomic bool pointers for suspension. However, this only works if I check it within every loop. Which is CPU costly.
Would using std::unique_guard be a solution as well?
To my knowledge, a mutex just tells the computer “hey these two guys can’t walk through the door at the same time?” or “hey this guy can’t walk in and out at the door at the same time?”
Any thoughts would help, I’ve heard people say don’t use a mutex on the main audio thread but I’ve seen a few scope locks in the juce code. So I think it’s possible, the other option would be manually flip a bool and have every block check whether or not to load the new info. Thank you.
You should not use a mutex in the audio thread code. You might get away with it if you can guarantee the other threads locking the mutex will never take a long time to complete what they are doing, but even then it would be better to not use the mutex. Juce indeed does have locks in some parts of its internal code, but that shouldn’t be taken as a blessing to use them in your own code too.
priority inversion: the realtime thread might have to wait for a lower priority thread to release the lock, hence falling below the low priority
some locks are implemented as system call, in which case calling the lock might itself not be realtime safe
Juce implements different kinds of Locks / CriticalSections, which would be beyond the scope here to disect. But they all have different mechanics and are usable in different situations. Just one example: SpinLock doesn’t do system calls like thread::notify() etc., but burns CPU instead.
I’d just like to add that all modern OSes use double lock checking for general purpose mutexes (for example JUCE’s CriticalSection), i.e. they first check if a lock is held with a cheap atomic memory operation. Only if it is contended do they need to call an expensive system call.
This means that a lock that is never contended is perfectly ok to use in realtime code. Obviously, a lock that is never contended is a bit useless, but we often need to use locks to guard against exceptional behaviour where a lock is only ever contended when the realtime guarantees are no longer valid anyway.
For example, if a lock in an audio callback guards against the audio device suddenly being removed (and it’s closed callback being called) then that’s perfectly fine. Obviously, it doesn’t matter anymore if the audio callback can fulfil it’s realtime guarantees when the audio device is no longer available. So it’s ok if the lock is contended in that situation.
Many of JUCE’s locks fall into this category. The remaining locks in realtime contexts are gradually being removed one-by-one and replaced with realtime safe paradigms.
I think it’s fair to assume that you don’t need to process during preset load, but locking the whole session would probably create bad results if user are auditioning presets while audio playing.
So — you need to fade out and not call you processing code so you’re free to mess with all your DSP. Checking a bool once per block shouldn’t be costly at all.
I’ve done it few different ways now — but currently I like to set a bool which triggers a fade out on the processor, and use a waitable event in the UI to synchronize the fade outs / ins around preset loads.
Trigger fade → wait → fade out done → edit → trigger in → wait → exit.
This avoids preset load functions which can get complex and rely on messaging. It’s also means you don’t need to defend against users loading a another preset before the last one finish — and it also enforces an attention to very fast preset loads so this all feels instant and doesn’t block your UI.
TLDR; waitable event is your friend here.
You may find you can sneak a preset load in between blocks if you have a very small and fast load function, but it might stop functioning acceptably at scale in a larger app