Short sounds not released after they finish playing

Hi!

I have been testing using FMOD with Unity for the last few days and I have run into a weird issue with releasing sounds. Simply put, if I play a long enough sound it will be released when it finishes playing or when I stop playing it. But for smaller sounds this is not the case. I find this weird because both sounds are played using the same FMOD Studio Event Emitter component.

Whether the sound is released seems to be dependent on the length of the sound. At least from my limited testing it doesn’t seem affected by either the file type or its original size. Anything that is over 20 seconds long gets released and anything below is not. The exception being any sounds in an audio table or sounds that are loaded through streaming.

I have also tried releasing the sounds manually by initiating the sound effect and adding a callback to it that would handle the release during the FMOD.Studio.EVENT_CALLBACK_TYPE.SOUND_STOPPED event, code below:

[AOT.MonoPInvokeCallback(typeof(FMOD.Studio.EVENT_CALLBACK))]
private static FMOD.RESULT SoundEventCallback(FMOD.Studio.EVENT_CALLBACK_TYPE type, IntPtr instancePtr, IntPtr parameterPtr)
{
    FMOD.Studio.EventInstance instance = new FMOD.Studio.EventInstance(instancePtr);
    switch (type)
    {
        case FMOD.Studio.EVENT_CALLBACK_TYPE.SOUND_STOPPED:
        {
            var sound = new FMOD.Sound(parameterPtr);
            var result = sound.release();
            sound.clearHandle();
            result = instance.release();
            instance.clearHandle();
            break;
        }
    }
    return FMOD.RESULT.OK;
}

This succeeds in releasing the sound but causes the following error whenever I try to play it again:
[FMOD] AsyncManager::asyncThreadLoop : System::update returned error 30.

Weirdly enough, it doesn’t do that for the longer sounds. Is there some optimization mechanism that just refuses to release shorter sounds for the sake of not loading them into memory again until their entire bank is unloaded?

Alternatively, is there a way to fix the above code to not cause the mentioned error? Since it mentions threads I suppose I am releasing the sound from another thread and I would need to somehow invoke this action on the main thread. Any tips are greatly appreciated.

Thanks in advance.

Used versions:
Unity version: 2021.2.0f1
FMOD for Unity package version: 2.02.04
FMOD Studio version: 2.02.04

Thanks in advance.

An error code of 30 is ERR_INVALID_HANDLE, which means you’ve somehow ended up with an object that has no link to the underlying API data. In this case, I would guess it’s because you’ve released the sound and instance but aren’t creating a new event instance when you try to play a new sound?
How is it that you came to the conclusion that shorter sound are not being released? They certainly should be released automatically after playing and shouldn’t be lingering in memory indefinitely.

Thanks for your reply jeff.

I am creating an event instance every time I try to play a sound by calling RuntimeManager.CreateInstance. I highly doubt that is the issue because the same code works for the longer sounds without causing the error (true in all cases, more on that below). Posting the entire class for more clarity:

public class SoundEventTrigger : MonoBehaviour
{
    public FMODUnity.EventReference eventReference;
    private FMOD.Studio.EVENT_CALLBACK eventCallback;
    private FMOD.Studio.EventInstance currentInstance;

    private void Start()
    {
        eventCallback = new FMOD.Studio.EVENT_CALLBACK(SoundEventCallback);
    }

    public void PlaySound()
    {
        var eventInstance = FMODUnity.RuntimeManager.CreateInstance(eventReference);
        currentInstance = eventInstance;
        eventInstance.setCallback(eventCallback);
        eventInstance.start();
    }

    public void StopSound()
    {
        currentInstance.stop(FMOD.Studio.STOP_MODE.IMMEDIATE);
    }

    [AOT.MonoPInvokeCallback(typeof(FMOD.Studio.EVENT_CALLBACK))]
    private static FMOD.RESULT SoundEventCallback(FMOD.Studio.EVENT_CALLBACK_TYPE type, IntPtr instancePtr, IntPtr parameterPtr)
    {
        FMOD.Studio.EventInstance instance = new FMOD.Studio.EventInstance(instancePtr);

        switch (type)
        {
            case FMOD.Studio.EVENT_CALLBACK_TYPE.SOUND_STOPPED:
            {
                var sound = new FMOD.Sound(parameterPtr);
                var result = sound.release();
                sound.clearHandle();
                result = instance.release();
                instance.clearHandle();
                break;
            }
        }
        return FMOD.RESULT.OK;
    }
}

As I mentioned, this works with the longer sounds, except when I try to create more than one event instance at a time. If one instance is stopped or finishes playing while there are other instances playing the same sound, it will correctly stop them due to the sound being released. But for some reason, despite the callback being called for all of them, this will not release the sound until the moment all of them would have finished playing naturally. If I try to start a new instance during this delay it results in the invalid handle error but if I wait for when the sound is actually released it works fine.

This is highlighted by the log messages where I call for release on the first instance, which results in OK result code. This is immediately followed by the handler being called for all the other instances where releasing the sound gives the invalid handle error since it has already been released (this has no negative impact on the game itself) followed by the log message: [FMOD] SoundI::release : <audio_file's_name> (000001617D5D39B8). After the delay the log message [FMOD] SoundI::release : (00000163DF98A378) appears, highlighting that the sound has actually been released. If there is only one instance at a time, the second message follows immediately after the first one.

This probably isn’t that big of a deal since either we will play unique sounds and for effects that play the same sounds I will have to build some sort of management system to control when a sound should be released by checking how many instances are using the given sound.

I used the log messages generated by the FMOD Unity Integration that are generated after setting the Logging Level to Log in the FMOD Settings. I double-checked this by using the FMOD Studio’s profiler and monitoring the RAM usage it displays. I will try to describe all four cases in detail:

  • Using FMOD Studio Event Emitter
    • Shorter sound
      According to the logs, the sound is not released when it finishes playing, neither when it gets stopped. This is confirmed by the fact that when I play it again there are no calls to create the sound. Profiler shows the memory not returning to the previous levels which confirms this.
    • Longer sound
      When the sound naturally finishes it is not released until the moment I try to play it again. At that point it for some reason releases the sound and loads it back into memory within the same frame (the profiler literally says so). When the sound gets stopped the emitter acts as expected and the sound is immediately released. Memory usage confirms both cases.
  • Using the API (above code)
    • Shorter sound
      Letting the sound play out or stopping it ends up in a call to release the sound but the second log message never appears. Trying to play the sound again results in the invalid handle error popping up every frame. In memory there is a slight decrease in used memory but it does not go back to the initial level.
    • Longer sound
      Either playing out or stopping the sound results in the sound being released (outside the already mentioned issue with multiple concurrent instances) as confirmed by the profiler.

I think the issue with the multiple instances is something I will have to manage on our side anyway, so I am not particularly concerned with that. The emitter has a weird behaviour but there might be a design decision behind that. Although not releasing the sound and then doing so just to load it back in again is weird. As for the API, I am still not sure what I could be doing wrong. It’s almost like some part of FMOD just refuses to release the shorter sound but another part thinks it just did so and in the end it tries to use the old handle that is somehow no longer valid?

The event I am using is Oneshot, its master track has persistence set to off and it contains just one single instrument with the given sound. This event is in a bank that doesn’t have sample data preloaded, so I would expect FMOD to release the sound, at least when I use the API.

Thank you for the extra info, I think I see what’s happening now- it looks like non-streaming sounds load the first time and no other time, presumably for optimization as you suggested, whereas streaming sounds need to be loaded and unloaded every time because of their size.
To allow the kind of dynamic sound releasing behaviour you are looking for you can try forcing a sample reload in your PlaySound method:

    public void PlaySound()
    {
        // Reload samples
        var eventDescription = FMODUnity.RuntimeManager.GetEventDescription(eventReference);
        eventDescription.unloadSampleData();
        eventDescription.loadSampleData();

        var eventInstance = FMODUnity.RuntimeManager.CreateInstance(eventReference);
        currentInstance = eventInstance;
        eventInstance.set3DAttributes(FMODUnity.RuntimeUtils.To3DAttributes(transform));
        eventInstance.setCallback(eventCallback);
        eventInstance.start();
    }

I found that this can cause issues if you call it too much- which makes sense at that point because if you are calling it often you aren’t going to want this kind of one-time behaviour anyway.

Thank You Guys… this solution worked for me…

1 Like