WatchOutput works correctly on the initial play, but after calling SetSound and Play again, the output callback never fires.

Hi,

I’m having an issue with UMetaSoundOutputSubsystem::WatchOutput() when changing a MetaSound dynamically on an UAudioComponent.


Issue

WatchOutput() works correctly on the initial play, but after calling SetSound() and Play() again, the output callback never fires.


Repro Code

C++


void UMyAudioComponent::SetMetaSound(USoundBase* NewSound)
{
    Stop();
    SetSound(NewSound);
    Play();

    GetWorld()->GetTimerManager().SetTimerForNextTick(
        this,
        &UMyAudioComponent::RegisterWatcher
    );
}

void UMyAudioComponent::RegisterWatcher()
{
    if (!IsPlaying())
    {
        return;
    }

    if (UMetaSoundOutputSubsystem* Subsystem =
        GetWorld()->GetSubsystem<UMetaSoundOutputSubsystem>())
    {
        Delegate.Unbind();
        Delegate.BindDynamic(this, &UMyAudioComponent::OnOutput);

        bool bResult = Subsystem->WatchOutput(
            this,
            TEXT("LastState"),
            Delegate
        );

        UE_LOG(LogTemp, Warning, TEXT("WatchOutput result: %s"),
            bResult ? TEXT("Success") : TEXT("Fail"));
    }
}

Show more lines


Observed Behavior

  • First Play → :white_check_mark: callback fires

  • After SetSound():cross_mark: callback never fires again

  • WatchOutput() still returns true


Question

Is WatchOutput() expected to work after calling SetSound() on an existing AudioComponent, or is it only valid for the initial MetaSound instance?


Thanks!

Hello @realRitT ,

My first guess is that binding your output watcher delegate after the next tick may still be too early. You could bind to the OnGeneratorInstanceInfoCreated delegate in your MetaSoundSource asset, register your watcher there and check if that’s late enough for it to work. That delegate will be called on that asset everytime a generator is created independently of the AudioComponent, but you have the AudioComponentId as a parameter for you to check if it’s being called for the AudioComponent instance you are on. Eg:

void UMyAudioComponent::SetMetaSound(USoundBase* NewSound)
{
    Stop();

    if (UMetaSoundSource* MetaSound = Cast<UMetaSoundSource>(NewSound))
    {
        MetaSound->OnGeneratorInstanceInfoCreated.RemoveAll(this);
        MetaSound->OnGeneratorInstanceInfoCreated.AddUObject(
            this,
            &UMyAudioComponent::OnGeneratorCreated);
    }

    SetSound(NewSound);
    Play();
}

void UMyAudioComponent::OnGeneratorCreated(const FGeneratorInstanceInfo& Info)
{
    if (Info.AudioComponentId != GetAudioComponentID())
    {
        return;
    }

    RegisterWatcher();
}

I didn’t write that code in-engine, it may not compile, but it’s just to give you an idea. Let me know if you try that out.