Need help to optimize load time of LoadAssetAsync in Addressables

Hi!

We have created a fairytale app/game for kids where they can listen to fairytales with background images and audio dialogues. The app is called “Moderna Sagor” (available in Finland and Sweden only) and is available on both GooglePlay and AppStore.

Because of the huge amount of audio and image files I chose to use Addressables. Each fairytale has one Addressables group for images and one group for audio dialogues). First time when starting a fairytale the two .bundle files (audio + image) are downloaded to the device.

All is good this far and it works fine (and fast) on my iPad Air (4th generation) with iOS 14.5.1 (and also fast on Androids). But when testing on my iPhone SE device (iOS 14.3), there will be a 2-3 second loading time for each dialogue when playing the fairytale number 4 which has 2299 audio files in the group. I have also tested on an older Android with (Android 7.0 installed) and the fairytale 4 has a little bit slower load time but not as bad as the iPhone SE. When playing the other fairytales with less number of files in the group, the load time is ok on all devices.

I have used Articy:draft to script all fairytales and all dialgues are automatically connected in Articy:draft (each sentence to one .wav file). The audio clip asset path are automatically set from Articy and stored in the object I receive from Articy:draft when changing to next dialogue (assetRefPath in code below). The dialogue text is also recevied from the same Articy object which will be displayed instantly on screen.

There seems to be a problem on older iOS devices when the number of files are higher, and a 2-3 seconds lookup time from Addressables for each sentence is not acceptable for me. Is there a way to optimize the LoadAssetAsync speed when having about 2300 audio files in an Addressables group?

I know I could split them to several bundle files but I would like to fix it in code since the Addressables system is said to be so fast. Maybe I’m doing something wrong…

822 audio files - Fairytale 1 (Origa och äventyret i stora granskogen)
628 audio files - Fairytale 2 (Jorden runt med Elize - sydamerika)
258 audio files - Fairytale 3 (Prins Huberts äventyr)
2299 audio files - Fairytale 4 (Mysteriet med den försvunna guldstatyn)
240 audio files - Fairytale 5 (Leilas bokstavsjakt)

All Addressables groups have the RemoteBuildPath, RemoteLoadPath and “Can Change Post Release” set.

Android & iOS audio settings:

  • Decompress on load
  • Preload Audio data checked
  • Vorbis format
  • Quality 100
  • Preserve sample rate

public IEnumerator LoadAudioClipCoroutine(string assetRefPath, IFlowObject obj, AutoPlayCallback callback)
{
// Release the old audio clip handle
ReleaseAddressableAudioClip();

// Load the wav file through Addressables
addressablesAudioClipHandle = Addressables.LoadAssetAsync(assetRefPath + “.wav”);
addressablesAudioClipHandle.Completed += OnAddressablesAudioClipLoaded;

// Wait for async to run
yield return addressablesAudioClipHandle;

// Ok, the clip is loaded async, play it!
PlayDialogueInternal(obj, callback);
}

public void ReleaseAddressableAudioClip()
{
// Release previous audio clip if we have one loaded
if (addressablesAudioClipHandle.IsValid())
Addressables.Release(addressablesAudioClipHandle);
}

void OnAddressablesAudioClipLoaded(AsyncOperationHandle handle)
{
switch (handle.Status)
{
case AsyncOperationStatus.Succeeded:
currentAudioClip = handle.Result;
Debug.Log(“AsyncOperationStatus.Succeeded - AudioClip " + currentAudioClip.name + " loaded!”);
break;
case AsyncOperationStatus.Failed:
Debug.LogError(“AudioClip load failed.”);
break;
default:
// case AsyncOperationStatus.None:
Debug.LogError(“AsyncOperationStatus.None”);
break;
}
}

Sorry for the unformatted code pasted earlier… here is a more readable version

public IEnumerator LoadAudioClipCoroutine(string assetRefPath, IFlowObject obj, AutoPlayCallback callback)
{
    // Release the old audio clip handle
    ReleaseAddressableAudioClip();

    // Load the wav file through Addressables
    addressablesAudioClipHandle = Addressables.LoadAssetAsync<AudioClip>(assetRefPath + ".wav");
    addressablesAudioClipHandle.Completed += OnAddressablesAudioClipLoaded;

    // Wait for async to run
    yield return addressablesAudioClipHandle;

    // Ok, the clip is loaded async, play it!
    PlayDialogueInternal(obj, callback);
}

public void ReleaseAddressableAudioClip()
{
    // Release previous audio clip if we have one loaded
    if (addressablesAudioClipHandle.IsValid())
        Addressables.Release(addressablesAudioClipHandle);
}

void OnAddressablesAudioClipLoaded(AsyncOperationHandle<AudioClip> handle)
{
    switch (handle.Status)
    {
        case AsyncOperationStatus.Succeeded:
            currentAudioClip = handle.Result;
            Debug.Log("AsyncOperationStatus.Succeeded - AudioClip " + currentAudioClip.name + " loaded!");
            break;
        case AsyncOperationStatus.Failed:
            Debug.LogError("AudioClip load failed.");
            break;
        default:
            // case AsyncOperationStatus.None:
            Debug.LogError("AsyncOperationStatus.None");
            break;
    }
}

Using LTS 2019.4.26f1 with Addressables 1.16.16

I’m not sure what specifically is the problem with the iPhone SE but I can give a few things to try. I would definitely try to break the bundle up into at least 3 or 4 smaller bundles for the chapter that has 2300 audio files. Another thing to try is your bundle compression - I’m not sure if your bundles are compressed or not, but if so, try with them uncompressed since the audio is compressed already. If that makes the bundles too large for you, make sure that you are using LZ4 compression and not LZMA. LZMA requires the entire bundle to be loaded into memory (or it is recompressed during download into lz4). Another tip to make sure that you aren’t delaying an unnecessary frame is to check IsDone on the returned operation and don’t yield or set the callback if it is done, just process it right there.

Hi Paul!
Thank you for your answers. I will try the different solutions you provided. The compression was set to LZ4, the uncompressed file size became nearly the same as LZ4 size … I will start testing with that.

Hi again!
Just wanted to update with results from my tests.

Uncompressed bundles instead of LZ4 did not make the LoadAssetAsync any faster for me on the iPhone SE device.

However, when I created 7 smaller bundles for the fairytale bundle with 2300 audio files (about 330 files in each), I got the result I wanted. The good thing was that I did not need to change the download code, because I kept the same label “audio04-se” for all files even if moving to 7 separate bundles. This way it downloaded all 7 audio bundles first time before playing the fairytale.
I will fix the IsDone check later to also save a frame. =)

It is probably some kind of string/hash search algorithm inside Unity’s LoadAssetAsync that could be optimized if using a large number of assets in a bundle (especially on older devices), I think the loading part is fast once it finds the correct asset.

Thank you for your help!