How to prevent UAudioComponent from being "nulled"

Hello there!

Let’s go to the point.

This is how I’m creating my component:
.h


UPROPERTY()
        UAudioComponent* AudioComponent;

.cpp


UAudioComponent* UAudioController::Sound2D(UObject* WorldContextObject, class USoundBase* Sound, float VolumeMultiplier, float PitchMultiplier, float StartTime)
{
    if (!Sound)
    {
        return nullptr;
    }

    AudioComponent = UGameplayStatics::CreateSound2D(WorldContextObject, Sound, VolumeMultiplier, PitchMultiplier, StartTime);
    PlaybackPositionMap.Add(AudioComponent, 0.f);
    BindDelegates(AudioComponent);
    AudioComponent->Play(StartTime);
    return AudioComponent;
}

Quick explain about BindDelegates and PlaybackPositionMap:

BindDelegates contains OnAudioPlaybackPercent and OnAudioFinished binds.

PlaybackPositionMap holds AudioComponent with float value which is self explaining. Just need access to time played.

What is the problem?

When I’ll just play sound it’s all great and delegates works perfectly, but if I’ll pause game for let’s say 1 minute, then come back and unpause it, suddenly my AudioComponent is null. It cannot be unpaused and played at all.

So my question is why is that and how can I prevent it from happening? I was thinking that reason is AudioComponent not being UPROPERTY() before but that also failed.

If there is any further information needed, let me know :slight_smile:

1 Like

Where is the pointer to the Audio Component stored? If the pointer is not inside another reflected type then it will still be GC’d regardless of whether it’s a UPROPERTY or not.

Ensure it also has bAutoDestroy set to false.

3 Likes

So my pointer is stored in this class


 

  1. #pragma once
  1. #include "CoreMinimal.h"
  1. #include "Components/ActorComponent.h"
  1. #include "AudioController.generated.h"
  1. USTRUCT(BlueprintType)
  1. struct FAudioComponentPlaybackPosition
  1. {
  1. GENERATED_BODY()
  1. UPROPERTY(BlueprintReadOnly, EditAnywhere, Category = "AudioController")
  1. UAudioComponent* AudioComponent;
  1. UPROPERTY(BlueprintReadOnly, EditAnywhere, Category = "AudioController")
  1. float PlaybackPosition;
  1. FAudioComponentPlaybackPosition()
  1. {
  1. AudioComponent = nullptr;
  1. PlaybackPosition = 0.f;
  1. }
  1. FAudioComponentPlaybackPosition(UAudioComponent* AComponent, float NewPlaybackPosition)
  1. {
  1. AudioComponent = AComponent;
  1. PlaybackPosition = NewPlaybackPosition;
  1. }
  1. };
  1. UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
  1. class MYGAME_API UAudioController : public UActorComponent
  1. {
  1. GENERATED_BODY()
  1. public:
  1. // Sets default values for this component's properties
  1. UAudioController();
  1. protected:
  1. // Called when the game starts
  1. virtual void BeginPlay() override;
  1. public:
  1. // Called every frame
  1. virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
  1. //UFUNCTION(BlueprintCallable, Category = "Test")
  1. // void LambdaTesting();
  1. UFUNCTION(BlueprintCallable, Category = "AudioController", meta=(HidePin="WorldContextObject", WorldContext="WorldContextObject"))
  1. UAudioComponent* Sound2D(UObject* WorldContextObject,class USoundBase* Sound, float VolumeMultiplier = 1.f, float PitchMultiplier = 1.f, float StartTime = 0.f);
  1. UFUNCTION(Category = "AudioController")
  1. void SavePlaybackPosition(const USoundWave* SoundWave, const float PlaybackPercent);
  1. UFUNCTION(Category="AudioController")
  1. void SoundFinished();
  1. UFUNCTION(Category = "AudioController")
  1. void BindDelegates(UAudioComponent* AudioComponent);
  1. UFUNCTION(Category = "AudioController")
  1. void UnbindDelegates(UAudioComponent* AudioComponent);
  1. //UFUNCTION(BlueprintCallable, Category = "AudioController", meta = (HidePin = "WorldContextObject", WorldContext = "WorldContextObject"))
  1. //UAudioComponent* LambdaSound2D(UObject* WorldContextObject, class USoundBase* Sound, FTimerHandle &TimerHandle, float VolumeMultiplier = 1.f, float PitchMultiplier = 1.f, float StartTime = 0.f);
  1. UFUNCTION(BlueprintImplementableEvent, Category = "AudioController")
  1. void OnAudioFinishedPlaying(UAudioComponent* AudioComponent);
  1. UFUNCTION(BlueprintCallable, Category = "AudioController")
  1. void PauseDialogue(bool SetPaused, FTimerHandle TimerHandle);
  1. UPROPERTY()
  1. TMap<UAudioComponent*, float> PlaybackPositionMap;
  1. UPROPERTY()
  1. UAudioComponent* AudioComponent;
  1. };

 

I will try to set bAutoDestroy to false. Also i googled what exactly reflection is and found this link: Unreal Property System (Reflection) - Unreal Engine
Does this mean I need change my GENERATED_BODY() to GENERATED_UCLASS_BODY() to make it know that it’s reflected type or there is more to this?

I think I’m witnessing this too. I have a very basic call to UGameplayStatics::SpawnSound2D and I store the return of that. I later check it, and if its valid, I made a FadeOut call. But apparently, it nulls out between the check and the call anyway so it crashes. Its very rare, but I’ve observed it with a dump too. UE4.26.2

void playsoundfunction() { m_MySound = UGameplayStatics::SpawnSound2D(...); }

void checklaterfunction()
{
   if(m_MySound)
   {
      m_MySound->FadeOut(0,0); // Crashes here will null exception occasionally. Strange!
   }
}

You’re checking and using different pointers.

My mistake, that’s just a typo of what I wrote in the forums. I fixed the typo. Thank you, unfortunately I do not have the typo in the code itself heh

That’s a very strange code style for working in Unreal. You should use IsValid() to check the ptr, and you should m ake sure the pointer is held in a UPROPERTY … and that it’s not auto destroying itself.

IsValid() also does the same thing, the only difference is that it checks for pending kill flag. Even swapping out the nullptr check with IsValid, the crash still occurs (again, not 100% of the time)

But, you may be onto something with auto destroy.

UGameplayStatics::SpawnSound2D has a default parameter of true for auto destroy. So is it possible that Unreal is destroying it in a different thread? It’s the only explanation I can think of bypassing a nullptr check and IsValid() If it was destroying on the same thread, then it would be null already or after the call, not during it.

When I investigate the mini dump in the debugger, the other threads are not doing anything. Specifically the audio thread is waiting at the time of the call. But that also doesn’t mean it didn’t do it prior to handling the destroy or something. Or it could be one of the many other threads doing something Im unfamiliar with.

1 Like

I want AudioComponent stay valid during whole session. bAutoDestroy=false works to me. Should I destroy it myself on the end of the session?

bAutoDestroy simply means that when the sound stops playing, the component destroys itself.

Disabling that, will not affect the rest of the component lifecycle.

For usual usecases this means you don’t need to manually call Destroy on it. However, if you have this component dynamically created by a parent component, then it might be useful to call Destroy on the DestroyComponent override of the parent component. (I say dynamically because statically created in a component constructor isn’t officially supported)

1 Like

Thank you for mention that, it’s exactly my case. I create UAudioComponent on BeginPlay of UActorComponent with UGameplayStatics::CreateSound2D function and Play/Stop it during the session.

Thank you very much. I was struggling to find the cause. It was because of auto-destroy for my case as well. Was also using UGameplayStatics::SpawnSound2D() function.