Files
Brandyn / Techy fcc1b09210 init
2026-04-04 15:40:51 -05:00

184 lines
9.8 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Subsystems/WorldSubsystem.h"
#include "Quartz/AudioMixerClockManager.h"
#include "Sound/QuartzQuantizationUtilities.h"
#include "QuartzSubsystem.generated.h"
// forwards
namespace Audio
{
class FMixerDevice;
class FQuartzClockManager;
class FQuartzShareableCommandQueue;
}
class FQuartzTickableObject;
class UQuartzClockHandle;
using MetronomeCommandQueuePtr = TSharedPtr<Audio::FQuartzShareableCommandQueue, ESPMode::ThreadSafe>;
struct FQuartzTickableObjectsManager : public FQuartLatencyTracker
{
public:
AUDIOMIXER_API void Tick(float DeltaTime);
AUDIOMIXER_API bool IsTickable() const;
AUDIOMIXER_API void SubscribeToQuartzTick(FQuartzTickableObject* InObjectToTick);
AUDIOMIXER_API void UnsubscribeFromQuartzTick(FQuartzTickableObject* InObjectToTick);
private:
// list of objects needing to be ticked by Quartz
TArray<FQuartzTickableObject *> QuartzTickSubscribers;
// index to track the next subscriber to tick (if updates are being amortized across multiple UObject Ticks)
int32 UpdateIndex{ 0 };
};
UCLASS(DisplayName = "Quartz", MinimalAPI)
class UQuartzSubsystem : public UTickableWorldSubsystem
{
GENERATED_BODY()
public:
// ctor/dtor
UQuartzSubsystem() = default;
virtual ~UQuartzSubsystem() override = default;
//~ Begin UWorldSubsystem Interface
AUDIOMIXER_API virtual void Initialize(FSubsystemCollectionBase& Collection) override;
AUDIOMIXER_API virtual void Deinitialize() override;
AUDIOMIXER_API virtual bool DoesSupportWorldType(EWorldType::Type WorldType) const override;
AUDIOMIXER_API void virtual BeginDestroy() override;
//~ End UWorldSubsystem Interface
//~ Begin FTickableGameObject Interface
AUDIOMIXER_API virtual void Tick(float DeltaTime) override;
AUDIOMIXER_API virtual bool IsTickableWhenPaused() const override;
AUDIOMIXER_API virtual bool IsTickable() const override;
AUDIOMIXER_API virtual TStatId GetStatId() const override;
//~ End FTickableGameObject Interface
// these calls are forwarded to the internal FQuartzTickableObjectsManager
AUDIOMIXER_API void SubscribeToQuartzTick(FQuartzTickableObject* InObjectToTick);
AUDIOMIXER_API void UnsubscribeFromQuartzTick(FQuartzTickableObject* InObjectToTick);
// get C++ handle (proxy) to a clock if it exists
AUDIOMIXER_API Audio::FQuartzClockProxy GetProxyForClock(FName ClockName) const;
// allow an external clock (not ticked by the Audio Mixer or QuartzSubsystem) to be accessible via this subsystem
AUDIOMIXER_API void AddProxyForExternalClock(const Audio::FQuartzClockProxy& InProxy);
// static methods
static AUDIOMIXER_API UQuartzSubsystem* Get(const UWorld* const World);
// Helper functions for initializing quantized command initialization struct (to consolidate eyesore)
static AUDIOMIXER_API Audio::FQuartzQuantizedRequestData CreateRequestDataForTickRateChange(UQuartzClockHandle* InClockHandle, const FOnQuartzCommandEventBP& InDelegate, const Audio::FQuartzClockTickRate& InNewTickRate, const FQuartzQuantizationBoundary& InQuantizationBoundary);
static AUDIOMIXER_API Audio::FQuartzQuantizedRequestData CreateRequestDataForTransportReset(UQuartzClockHandle* InClockHandle, const FQuartzQuantizationBoundary& InQuantizationBoundary, const FOnQuartzCommandEventBP& InDelegate);
static AUDIOMIXER_API Audio::FQuartzQuantizedRequestData CreateRequestDataForStartOtherClock(UQuartzClockHandle* InClockHandle, FName InClockToStart, const FQuartzQuantizationBoundary& InQuantizationBoundary, const FOnQuartzCommandEventBP& InDelegate);
static AUDIOMIXER_API Audio::FQuartzQuantizedRequestData CreateRequestDataForSchedulePlaySound(UQuartzClockHandle* InClockHandle, const FOnQuartzCommandEventBP& InDelegate, const FQuartzQuantizationBoundary& InQuantizationBoundary);
static AUDIOMIXER_API Audio::FQuartzQuantizedRequestData CreateRequestDataForQuantizedNotify(UQuartzClockHandle* InClockHandle, const FQuartzQuantizationBoundary& InQuantizationBoundary, const FOnQuartzCommandEventBP& InDelegate, float InMsOffset = 0.f);
UFUNCTION(BlueprintCallable, Category = "Quartz Clock Handle")
AUDIOMIXER_API bool IsQuartzEnabled();
// Clock Creation
// create a new clock (or return handle if clock already exists)
UFUNCTION(BlueprintCallable, Category = "Quartz Clock Handle", meta = (WorldContext = "WorldContextObject", AdvancedDisplay = "bUseAudioEngineClockManager"))
AUDIOMIXER_API UQuartzClockHandle* CreateNewClock(const UObject* WorldContextObject, FName ClockName, FQuartzClockSettings InSettings, bool bOverrideSettingsIfClockExists = false, bool bUseAudioEngineClockManager = true);
// delete an existing clock given its name
UFUNCTION(BlueprintCallable, Category = "Quartz Clock Handle", meta = (WorldContext = "WorldContextObject"))
AUDIOMIXER_API void DeleteClockByName(const UObject* WorldContextObject, FName ClockName);
// delete an existing clock given its clock handle
UFUNCTION(BlueprintCallable, Category = "Quartz Clock Handle", meta = (WorldContext = "WorldContextObject"))
AUDIOMIXER_API void DeleteClockByHandle(const UObject* WorldContextObject, UPARAM(ref) UQuartzClockHandle*& InClockHandle);
// get handle for existing clock
UFUNCTION(BlueprintCallable, Category = "Quartz Clock Handle", meta = (WorldContext = "WorldContextObject"))
AUDIOMIXER_API UQuartzClockHandle* GetHandleForClock(const UObject* WorldContextObject, FName ClockName);
// returns true if the clock exists
UFUNCTION(BlueprintCallable, Category = "Quartz Clock Handle", meta = (WorldContext = "WorldContextObject"))
AUDIOMIXER_API bool DoesClockExist(const UObject* WorldContextObject, FName ClockName);
// returns true if the clock is running
UFUNCTION(BlueprintCallable, Category = "Quartz Clock Handle", meta = (WorldContext = "WorldContextObject"))
AUDIOMIXER_API bool IsClockRunning(const UObject* WorldContextObject, FName ClockName);
// Returns the duration in seconds of the given Quantization Type
UFUNCTION(BlueprintCallable, Category = "Quartz Clock Handle", meta = (WorldContext = "WorldContextObject"))
AUDIOMIXER_API float GetDurationOfQuantizationTypeInSeconds(const UObject* WorldContextObject, FName ClockName, const EQuartzCommandQuantization& QuantizationType, float Multiplier = 1.0f);
// Retrieves a timestamp for the clock
UFUNCTION(BlueprintCallable, Category = "Quartz Clock Handle", meta = (WorldContext = "WorldContextObject"))
AUDIOMIXER_API FQuartzTransportTimeStamp GetCurrentClockTimestamp(const UObject* WorldContextObject, const FName& InClockName);
// Returns the amount of time, in seconds, the clock has been running. Caution: due to latency, this will not be perfectly accurate
UFUNCTION(BlueprintCallable, Category = "Quartz Clock Handle", meta = (WorldContext = "WorldContextObject"))
AUDIOMIXER_API float GetEstimatedClockRunTime(const UObject* WorldContextObject, const FName& InClockName);
// latency data (Game thread -> Audio Render Thread)
UFUNCTION(BlueprintCallable, Category = "Quartz Subsystem", meta = (WorldContext = "WorldContextObject"))
AUDIOMIXER_API float GetGameThreadToAudioRenderThreadAverageLatency(const UObject* WorldContextObject);
UFUNCTION(BlueprintCallable, Category = "Quartz Subsystem", meta = (WorldContext = "WorldContextObject"))
AUDIOMIXER_API float GetGameThreadToAudioRenderThreadMinLatency(const UObject* WorldContextObject);
UFUNCTION(BlueprintCallable, Category = "Quartz Subsystem", meta = (WorldContext = "WorldContextObject"))
AUDIOMIXER_API float GetGameThreadToAudioRenderThreadMaxLatency(const UObject* WorldContextObject);
// latency data (Audio Render Thread -> Game thread)
UFUNCTION(BlueprintCallable, Category = "Quartz Subsystem")
AUDIOMIXER_API float GetAudioRenderThreadToGameThreadAverageLatency();
UFUNCTION(BlueprintCallable, Category = "Quartz Subsystem")
AUDIOMIXER_API float GetAudioRenderThreadToGameThreadMinLatency();
UFUNCTION(BlueprintCallable, Category = "Quartz Subsystem")
AUDIOMIXER_API float GetAudioRenderThreadToGameThreadMaxLatency();
// latency data (Round trip)
UFUNCTION(BlueprintCallable, Category = "Quartz Subsystem", meta = (WorldContext = "WorldContextObject"))
AUDIOMIXER_API float GetRoundTripAverageLatency(const UObject* WorldContextObject);
UFUNCTION(BlueprintCallable, Category = "Quartz Subsystem", meta = (WorldContext = "WorldContextObject"))
AUDIOMIXER_API float GetRoundTripMinLatency(const UObject* WorldContextObject);
UFUNCTION(BlueprintCallable, Category = "Quartz Subsystem", meta = (WorldContext = "WorldContextObject"))
AUDIOMIXER_API float GetRoundTripMaxLatency(const UObject* WorldContextObject);
UFUNCTION(BlueprintCallable, Category = "Quartz Subsystem")
AUDIOMIXER_API void SetQuartzSubsystemTickableWhenPaused(const bool bInTickableWhenPaused);
// sharable to allow non-UObjects to un-subscribe if the Subsystem is going to outlive them
AUDIOMIXER_API TWeakPtr<FQuartzTickableObjectsManager> GetTickableObjectManager() const;
private:
// deletes proxies to clocks that no longer exists
AUDIOMIXER_API void PruneStaleProxies();
static AUDIOMIXER_API void PruneStaleProxiesInternal(TArray<Audio::FQuartzClockProxy>& ContainerToPrune);
// sharable tickable object manager to allow for non-UObject subscription / un-subscription
TSharedPtr<FQuartzTickableObjectsManager> TickableObjectManagerPtr { MakeShared<FQuartzTickableObjectsManager>() };
// Clock manager/proxy-related data that lives on the AudioDevice for persistence.
TSharedPtr<Audio::FPersistentQuartzSubsystemData> ClockManagerDataPtr { nullptr };
bool bTickEvenWhenPaused = false;
// helpers
AUDIOMIXER_API Audio::FQuartzClockProxy* FindProxyByName(const FName& ClockName);
AUDIOMIXER_API Audio::FQuartzClockProxy const* FindProxyByName(const FName& ClockName) const;
AUDIOMIXER_API Audio::FQuartzClockManager* GetClockManager(const UObject* WorldContextObject, bool bUseAudioEngineClockManager = true);
}; // class UQuartzGameSubsystem