I have some parameters which should not get changed during a processBlock callback. Currently I am using one dummy variable and one dummy flag (for each parameter) to make sure that they get changed at the start of processBlock. For example:
process(juce::AudioBuffer<FloatType> &buffer) {
if (idxSamplerToReset.load()) { // if the idx of over-sampler has been reset by a APVTS listener
setOversampleID(tempIdxSampler.load()); // load the dummy variable and change the actual parameter
}
It works (in an ugly way). However, I came over another topic here:
I am wondering whether AudioProcessor::suspendProcessing() would be a better solution here. According to the documentation, suspendProcessing (true) blocks audio processing callback. But what will happen if processBlock is currently working on a buffer? It seems that processBlock will continue working on the buffer, which will cause problems in my situation.
threads::SpinLock() also seems like a solution, cause I am able to lock it in both audio thread and listener thread. Do you have any suggestions for this?
What’s the actual problem you’re trying to solve?
Technically the host is free to update your parameters at any time, which may be as frequently as every sample with automation. But in that case, the host should update the parameters, call processBlock with a buffer 1 sample long, update the parameters, call processBlock with a buffer 1 sample long, etc etc so that the processBlock calls are synchronized with the parameter changes.
It seems that I am not clear enough. For example, I am using juce built-in oversampler. Therefore, the oversampling rate should be consistent in one audio thread callback (for one processSampleUp and one processSampleDown). As you say, the host (listener) may change it at anytime.
Sure, but even if it happens to change during the execution of processBlock, that doesn’t mean that would actually affect the processBlock. Typically I read all parameter values into local variables at the start of processBlock and just use those for the entire callback.
That’s a good point. However, the over-sampling rate also affects the sampling rate of all operations on the over-sampled block. I have to call prepare(spec) on all these operations (dsp units) when the over-sampling rate changes.
So then I would probably listen for changes to that parameter on the main thread, and when it changes, do as you were thinking and suspend processing, call prepareToPlay, then unsuspend processing. But within the actual processBlock call I wouldn’t try to react to those changes at all.
You are correct. I am looking for an elegant way to suspend processing. Suppose the host(listener) changes the parameter at t=t1, the ideal working flow is:
at t2, listener should not perform changes until the first processBlock ends
at t3, processor should not process the second block until the listener completes changes
I am not sure whether AudioProcessor::suspendProcessing() meets the first requirement. threads::SpinLock() seems reasonable to me, but I haven’t seen any similar usages before
AudioProcessor::suspendProcessing already locks itself, so it can be considered a safe way to get the processing suspended.
suspendProcessing(true) is however a pretty blunt way to deal with the plugin state changes as it simply makes the plugin output silence until suspendProcessing(false) is called. Which might of course be good enough for your purposes…
Thanks for your reply. Could you please tell me what will happen to the block which the audio thread is processing (executing processBlock()) when the listener thread calls suspendProcessing(true)? If the audio thread continues processing current block, the parameter changing will cause some issues (because the inner buffer sizes of dsp units changes).
If you need to do something time-consuming on a thread and would like to make sure the audio processing callback doesn’t happen until you’ve finished, use this to disable the callback and re-enable it again afterwards.
If the processBlock is in the middle of the processing, the lock in suspendProcessing() will wait until the processBlock call has finished and it only then changes the suspended state. (That obviously isn’t the ideal way to deal with things in many cases, but if you don’t repeatedly call suspendProcessing that often, it should work fine.)
The code makes sure that the RMSSize is not updated during audio thread callback. For time-consuming tasks, you may consider suspendProcessing() instead.