We load multiple banks from different FMOD projects into our sound environment to handle DLC. When we load a new user vehicle, we have to replace the entire mixing environment. To do this we:
Stop all playing sounds.
Release all instances that we have handles to.
Unload all banks.
We then load the new master bank for the new vehicle and
Load all other unrelated banks that were used in the current level.
Recreate all instances.
We do this complete tear-down and build-up because the sounds in other banks might attach to different DSP chains depending on the bus mixing of the newly loaded master bank.
We discovered today that bank unload is asynchronous, and a particularly heavy bank our sound designer created doesnāt actually unload before we reload it (in step 5) - we then get a duplicate bank error.
Our questions are:
Is an unload/reload of these banks necessary? Or is it adequate to destroy and re-create the instances? We expect the entire mixer is being rebuilt by loading a new master bank.
If the unload and reload of these extra banks is necessary, what is the best way to block until the bank reload completes? Do we need to call āupdateā on the system repeatedly? Is the bank unload guaranteed to complete in bounded time? Is there any way to use the waiting thread to speed the process up?
Itās hard to advise regarding whether you need to unload everything or not as it depends on how your DLC or UGC banks change the mix. If the user generated content is purely additional you shouldnāt need to unload all banks, just the UGC banks. If the user generated content can change the global mix, then unfortunately yes, you will need to unload everything to get back to a clean state.
Unloading and loading of banks are processed in-order asynchronously, Iām not seeing how you got a duplicate bank error. If you want to perform a blocking operation to ensure all async commands and bank processing is complete, use System::flushCommands.
Anyway, System::flushCommands does not seem to be blocking at all. When we call UNLOAD, the bankās state goes to āUnloadingā. Then we call System::Update and System::flushCommands and check the state again and itās still āUnloadingā. Iāve even put a tight while-loop around the Update/flushCommands/getLoadingState loop to see if it eventually finishes and the state never transitions to anything past āUnloadingā. Weāve also tried calling System::flushSampleLoading() which didnāt seem to help either.
Ok after a ton of trial and error hereās what Iāve learned.
Running the while-loop I mentioned above leads to flushCommands EVENTUALLY blocking (After many calls)ā¦but it blocks forever and hangs.
This only happens with this one specific bank. So we tried removing things from the bank until it worked which lead us to a single referenced event. But it doesnāt appear to be a problem with how weāve made the event, it seems to be the event itself.
In other words, just cloning that reference event and using that clone instead works just fine. If we go back to the original referenced event it hangs.
If this is of interest to you, I can send someone the bank and project to look at. We looked at the difference between the problem-event and the cloned one and the only differences we see in the XML is the GUIDS but maybe we missed something.
Ok I just uploaded it. Itās fmod_project.zip. Let me know if you need more information. The referenced event is the āRoadsā event thatās part of the /environment/ambiance/ambiance event.
So the application starts the /environment/ambiance/ambiance event and as long as āRoadsā is triggered, the bad behavior occurs. If Roads is never triggered, or not referenced or cloned and renamed, it behaves properly.
Iāve put your project and event into one of our examples but I cannot reproduce the issues you are seeing, here is the code I am using, can you spot anything different from what you are doing?
I tried your sample code and I too couldnāt seem to get it to fail like our app no matter what I tried.
I did fix our app however. We discovered that we were not stopping event instances before releasing them on our teardown path toward a bank unload. We thought we were but we were not. Once I added the stop before the release, the bank seems to unload rapidly as youād expect.
What still doesnāt make sense to me is why I canāt replicate this behavior by removing the āstopā from your sample app. I also tried removing the releaseā¦doing anything I could to cause the bank unload to hang but it doesnāt do it. Is there protection in place to kill events that are still running when an unload is called? If so, itās odd that it kicked in for the sample app but not ours. If not, why doesnāt the sample app hang without stop being called?
Another question is what weāre supposed to do with one-shot events. The docs say that they continue until their natural end and we certainly donāt want to wait for that when weāre trying to unload a bank. We donāt bookkeep the one-shots. We fire and forget them so thereās no easy way for us to stop them like there is for looping events. Is there something else we can do thatās sufficient like calling stop on a bus? Or do we need to start bookkeeping these one-shots?
When you unload a bank it will destroy any Events that only exist in that bank, if they also exist in other banks they will persist. So for your case of unloading all banks, no Events should survive, you shouldnāt need to do any extra bookkeeping for one-shots, unload should take care of it.
If you need an explicit mechanism for stopping everything though, consider Studio::Bus::stopAllEvents on the master bus however it shouldnāt be necessary.
If you manage to reproduce the game behavior in the sample Iād be keen to dig into why itās not working for you.