How do I know that Bank has finished loading?

I’m trying to make on-demand Bank loading for a Unity project for WebGL.

I have read the following article:

  1. https://fmod.com/docs/2.02/unity/platform-specifics.html#async-loading
  2. https://fmod.com/docs/2.02/unity/examples-async-loading.html

These articles use a while loop to wait for the Bank to finish loading, but apparently this is not flexible enough.
For example, I have two Events that need to be played and they belong to different Banks. I want the Event to start playing as soon as the Bank it needs is loaded, whereas using the HaveAllBanksLoaded() method means that I have to wait for all the Banks to finish loading before I can play the Event.

It seems like adding a callback function to the LoadBank method would be a good way to do this, but it would require me to make some changes to the RuntimeManager code, and handling this part of the functionality could become cumbersome if and when we subsequently update the version of the FMOD Unity integration.
Other than that, is there a better way for me to accomplish this?


Also, I found a line of code in the RegisterLoadedBank method in the RuntimeManager that looks like this:
Instance.loadedBanks.Add(bankName, loadedBank);
According to the C# documentation, the Add method of a Dictionary throws an exception if the same value is passed in.

And before that, only the Instance.loadedBanks.ContainsKey(bankId) check was passed at the beginning of the LoadBank() method. And the execution of RegisterLoadedBank() method in loadFromWeb() method is after yield return www.SendWebRequest();. Is this likely to trigger an exception due to asynchronous loading?

As a side note, there is also a HasBankLoaded() method in the RuntimeManager that checks to see if a particular Bank has finished loading.
But this also requires the use of a while loop to check when the loading has finished.
In my opinion, I would try to avoid using high frequency while loops to check in my code. Especially since I might need to call this method in another language (e.g. in TypeScript) than in the C# code of the Unity project.

This is actually already the default behavior of FMOD. When you load a bank, only the bank metadata is loaded. The event’s sample data is then loaded when a call to Studio::EventDescription::createInstance is made, and a that point, the event will be ready to start playing. So, from your description, I don’t think there is no need for you to create a system that dynamically plays events once a bank is loaded.

When registering the newly loaded bank, we check the load result. If that bank has already been loaded, we don’t add it to the dictionary, so I don’t see anything that would cause a bank to be double-added to that Dictionary.

Perhaps you could move the while loop into a coroutine, as we do in our Asynchronous Loading example. The while loops in there aren’t high frequency- they only run once per frame and then yield if the bank hasn’t finished loading. Alternatively, you can call Studio::System::flushSampleLoading, which will block until all sample loading has completed.

1 Like

I just noticed that I missed the message you replied to.

There is a caveat about my question: this is a project under the WebGL platform.
Therefore, the loading of Bank at this point causes the entire Bank file to exist in memory, rather than being loaded on demand.

Ultimately our solution was to extend the custom method in the RuntimeManager class and trigger the callback method when loading the Bank is complete.

        private IEnumerator LoadBankFromURL(Action loadedCallback, string bankURL, string bankName, bool loadSamples)
        {
            byte[] loadWebResult;
            FMOD.RESULT loadResult;

            UnityEngine.Networking.UnityWebRequest www = UnityEngine.Networking.UnityWebRequest.Get(bankURL);
            yield return www.SendWebRequest();
            loadWebResult = www.downloadHandler.data;

            LoadedBank loadedBank = new LoadedBank();
            loadResult = Instance.studioSystem.loadBankMemory(loadWebResult, FMOD.Studio.LOAD_BANK_FLAGS.NORMAL, out loadedBank.Bank);
            if (loadResult != FMOD.RESULT.OK)
            {
                RuntimeUtils.DebugLogWarningFormat("[FMOD] loadFromWeb.  Path = {0}, result = {1}.", bankURL, loadResult);
            }
            RegisterLoadedBank(loadedBank, bankURL, bankName, loadSamples, loadResult);
            Instance.loadingBanksRef--;

            RuntimeUtils.DebugLogFormat("[FMOD] Finished loading {0}", bankURL);

            loadedCallback();
        }

        /// <summary>
        /// 扩展的方法。通过给定URL来加载bank文件。基于官方的LoadBank方法修改。
        /// </summary>
        /// <param name="loadedCallback">加载完成后的回调</param>
        /// <param name="bankURL">文件的URL。</param>
        /// <param name="bankName">bank文件的名称,应保证所有加载的bank文件的名称相互唯一。</param>
        /// <param name="loadSamples">是否加载SampleData。</param>
        public static void LoadBankByURL(Action loadedCallback, string bankURL, string bankName, bool loadSamples = false)
        {
            // bankId实际上就是用了bankName
            string bankId = bankName;

            // 如果已经加载过,则添加引用计数
            if (Instance.loadedBanks.ContainsKey(bankId))
            {
                ReferenceLoadedBank(bankId, loadSamples);
                // 也代表加载成功,直接调用回调
                loadedCallback();
            }
            else
            {
                // 添加计数,该字段是本类用于判断是否已经加载完成所有的bank文件的参考
                Instance.loadingBanksRef++;

                Instance.StartCoroutine(Instance.LoadBankFromURL(loadedCallback, bankURL, bankName, loadSamples));
            }
        }

        private IEnumerator LoadBankFromFilePath(Action loadedCallback, string bankPath, string bankId, bool loadSamples = false)
        {
            LoadedBank loadedBank = new LoadedBank();
            FMOD.RESULT loadResult = Instance.studioSystem.loadBankFile(bankPath, FMOD.Studio.LOAD_BANK_FLAGS.NORMAL, out loadedBank.Bank);
            Instance.RegisterLoadedBank(loadedBank, bankPath, bankId, loadSamples, loadResult);

            yield return 0;
            Instance.loadingBanksRef--;

            RuntimeUtils.DebugLogFormat("[FMOD] Finished loading {0}", bankPath);

            loadedCallback();
        }

        /// <summary>
        /// 扩展的方法。通过给定路径来加载bank文件。基于官方的LoadBank方法修改。
        /// </summary>
        /// <param name="loadedCallback">加载完成后的回调</param>
        /// <param name="bankPath">文件所在路径。</param>
        /// <param name="bankName">bank文件的名称,应保证所有加载的bank文件的名称相互唯一。</param>
        /// <param name="loadSamples">是否加载SampleData。</param>
        public static void LoadBankByFilePath(Action loadedCallback, string bankPath, string bankName, bool loadSamples = false)
        {
            // bankId实际上就是用了bankName
            string bankId = bankName;

            // 如果已经加载过,则添加引用计数
            if (Instance.loadedBanks.ContainsKey(bankId))
            {
                ReferenceLoadedBank(bankId, loadSamples);
                // 也代表加载成功,直接调用回调
                loadedCallback();
            }
            else
            {
                // 添加计数,该字段是本类用于判断是否已经加载完成所有的bank文件的参考
                Instance.loadingBanksRef++;

                Instance.StartCoroutine(Instance.LoadBankFromFilePath(loadedCallback, bankPath, bankId, loadSamples));
            }
        }