I’m wondering if I could get some advice. Our audio engine uses a number of DSPs for each channel played. Some are custom DSPs, and others are built-in FMOD effects. I wrap these in a custom object, which differs based on type (i.e. each custom DSP type is its own class, while the built-in types simply use a generic wrapper). The class interface is defined with the FMOD DSP callback, allowing FMOD to process data using these objects.
Here’s the problem – I’ve been having a lot of problems syncronizing between the destruction of the channel, along with the destruction of the DSP wrapper objects. Often, FMOD appears to be in the middle of a read callback when the object is destroyed. Or, maybe it has just been destroyed, so the read callback gets garbage.
Ok, so, I can solve this. Instead of storing the object pointer direct, I store a handle which I can now validate. And, I now wrap access to that handle in critical sections. Unfortunately, I appear to have introduced a deadlock when the callback is in the middle of the read callback, and then I try to release the DSP (no big surprise, I guess).
So, here’s my question: How would you recommend I approach releasing DSPs to make sure the DSP’s callback is no longer currently or going to be accessed by FMOD’s internal threads? I am calling Channel::stop() before attempting to release the DSPs, but I’d guess this isn’t a blocking call, in which case I can’t guarantee that the DSP’s callback function isn’t currently being used. Is there some function that I can use to check and make sure the DSP is no longer in use? Do I need to manually disconnect the DSPs before releasing them, for instance?
This has been somewhat problematic for me so far, and I’m wondering if I’ve missed a simple solution…
- JamesB asked 5 years ago
When you call stop or release, or even disconnect/remove, the operation is queued, and you may be inside the read callback as you execute those main thread calls.
I would defer the release of the objects like you say, but you need to know when it is ready to release.
You can firstly force the synchronization and make sure the read callback is not being executed with System::lockDSP and System::unlockDSP, but that is a blocking/stalling operation. Depends if you need it to be non blocking or not.
If you can’t take a stall, then you’ll have to do sometihng like
1. Call stop
2. wait a frame or 2 for the mixer to execute the mixer thread
3. release the custom dsp
Step 2 could be done by polling System::getDSPClock to see if it updates once (maybe twice to be safe in case of a race condition on the first time).
Oh brother, it’s right there in the documentation (I’m sure you’re used to that). But it’s good to know about the non-blocking option as well. I’ll probably start with the safer blocking method. It’s called from a background thread, so a brief stall won’t hurt too badly. I’ll keep the non-blocking option as a notion for when I need to do a bit more optimization.
Thanks for the response!
- JamesB answered 5 years ago
Please login first to submit.