Code-spawned sounds destroyed with no OnAudioFinished

Hi,

I’m spawning sounds in c++ and keeping the pointer to them in order to add effects to them later. I also register an OnAudioFinished callback to destroy the sounds once they’re finished and remove their pointer from the list.

At some point in the game, I want to stop and destroy all of the sounds. This usually works with no problem, but very rarely, one of my sound pointers is invalid, while still being non-null, meaning that I never got the OnAudioFinished callback.

I’ve tried many things to try to work around it, but nothing works. I’ve made sure I wasn’t destroying sounds any other way, the code is pretty straightforward. Has anyone ever had this issue or something similar?

// Spawn sound with autoDestroy to false
UAudioComponent* tmp = UGameplayStatics::SpawnSoundAtLocation(this, sound, location, FRotator::ZeroRotator, volume, pitch, 0.0f, nullptr, nullptr, false);

// Register the callback using OnAudioFinishedNative instead of OnAudioFinished because the sound pointer is a parameter of the callback.
tmp->OnAudioFinishedNative.AddUFunction(this, FNAME_ON_AUDIO_FINISHED);
m_allSounds.Push(tmp);

...

void MyClass::OnAudioFinished(UAudioComponent* audioComponent)
{
        // code to find sound in m_allSounds
...
        // Stop and destroy sound.
        // Crashes here on the first access to the pointer.
        sound->OnAudioFinishedNative.RemoveAll(this);
        if (sound->IsPlaying())
            sound->Stop();
        if (!sound->IsBeingDestroyed())
            sound->DestroyComponent();
        m_allSounds.RemoveAt(index);
}

...

// Code to destroy all the sounds.
for (AllSoundsStruct& sound : m_allSounds)
{
    // Shouldn't happen.
    if (!sound.m_sound)
        continue;
    sound.m_sound->OnAudioFinishedNative.RemoveAll(this);
    if (sound.m_sound->IsPlaying())
        sound.m_sound->Stop();
    if (!sound.m_sound->IsBeingDestroyed())
        sound.m_sound->DestroyComponent();
}

Seriously, I’ve tried everything. Mutexes, using UObjects and AddDynamic, autoDestroy, and a million ways of doing the same thing differently… Moral of the story is, if I try to stop any sound manually, it’ll start causing AudioComponents to start being destroyed/corrupted without notifying me.

To be clear, the code I posted at the top is more like pseudo code, but you get the idea.

struct AllSoundsStruct
{
    UAudioComponent* m_sound;
    float m_initialPitch;
};

TArray<AllSoundsStruct> m_allSounds;

My code is currently a wasteland after the dozens of workarounds I tried to implement and also it can take up to 4 hours to reproduce the issue, so it might take a bit of time but I’ll try. I hold very little faith in this though :stuck_out_tongue:

Can you show me definition of AllSoundsStruct and m_allSounds?

Okay, could you try switching your struct to a USTRUCT() and the pointer to the sound to a UPROPERTY() and try again?

I had a similar problem before so i know what it’s like to try everything and being frustrated.

Anyway, the only explanation that seemed probable at the time when a UPROPERTY() fixed my problem is as the documentation says, an Object reference stored in a raw pointer will be unknown to the Unreal Engine and will not prevent garbage collection. The pointer gets nulled before the code reaches the OnAudioFinished callback or your custom destruction code.

Sounds promising I’ll give you that.

It does seem like my issue, in part, was missing UPROPERTY() tags, probably… I also use the AudioComponents in a non-UObject class and this is preventing me from using UPROPERTY() in this case. So I might have to refactor that. I tried to use AddToRoot() to bypass that, but it turns out that attached sounds are actually not “stopped” when the component they’re attached to is destroyed, they’re destroyed (even if you set it to NOT autoDestroy), which asserts when RemoveFromRoot() hasn’t been called, and there’s nothing I can do. I think I’ll be able to fix the issue with some code refactoring, unless my time consuming tests prove that it wasn’t the issue…