Files
UnrealEngine/Engine/Source/Runtime/AudioMixer/Private/AudioMixerSourceManager.h
2025-05-18 13:04:45 +08:00

658 lines
24 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "AudioBusSubsystem.h"
#include "AudioMixerBuffer.h"
#include "AudioMixerBus.h"
#include "AudioMixerDevice.h"
#include "AudioMixerSourceOutputBuffer.h"
#include "AudioMixerSubmix.h"
#include "AudioMixerTrace.h"
#include "Containers/MpscQueue.h"
#include "DSP/BufferVectorOperations.h"
#include "DSP/EnvelopeFollower.h"
#include "DSP/InterpolatedOnePole.h"
#include "DSP/ParamInterpolator.h"
#include "DSP/RuntimeResampler.h"
#include "IAudioExtensionPlugin.h"
#include "ISoundfieldFormat.h"
#include "Misc/SpinLock.h"
#include "Sound/SoundModulationDestination.h"
#include "Sound/QuartzQuantizationUtilities.h"
#include "Stats/Stats.h"
#include "AudioMixerSourceManager.generated.h"
// Default this to on (it's quite a small memory footprint).
#ifndef WITH_AUDIO_MIXER_THREAD_COMMAND_DEBUG
#define WITH_AUDIO_MIXER_THREAD_COMMAND_DEBUG (1)
#endif //WITH_AUDIO_MIXER_THREAD_COMMAND_DEBUG
// Tracks the time it takes to up the source manager (computes source buffers, source effects, sample rate conversion)
DECLARE_CYCLE_STAT_EXTERN(TEXT("Source Manager Update"), STAT_AudioMixerSourceManagerUpdate, STATGROUP_AudioMixer, AUDIOMIXER_API);
// The time it takes to compute the source buffers (handle decoding tasks, resampling)
DECLARE_CYCLE_STAT_EXTERN(TEXT("Source Buffers"), STAT_AudioMixerSourceBuffers, STATGROUP_AudioMixer, AUDIOMIXER_API);
// The time it takes to process the source buffers through their source effects
DECLARE_CYCLE_STAT_EXTERN(TEXT("Source Effect Buffers"), STAT_AudioMixerSourceEffectBuffers, STATGROUP_AudioMixer, AUDIOMIXER_API);
// The time it takes to apply channel maps and get final pre-submix source buffers
DECLARE_CYCLE_STAT_EXTERN(TEXT("Source Output Buffers"), STAT_AudioMixerSourceOutputBuffers, STATGROUP_AudioMixer, AUDIOMIXER_API);
// The time it takes to process the HRTF effect.
DECLARE_CYCLE_STAT_EXTERN(TEXT("HRTF"), STAT_AudioMixerHRTF, STATGROUP_AudioMixer, AUDIOMIXER_API);
// For diagnostics, keep track of what phase of updating the Source manager is in currently.
UENUM()
enum class ESourceManagerRenderThreadPhase: uint8
{
Begin,
PumpMpscCmds,
PumpCmds,
ProcessModulators,
UpdatePendingReleaseData,
GenerateSrcAudio_WithBusses,
ComputeBusses,
GenerateSrcAudio_WithoutBusses,
UpdateBusses,
SpatialInterface_OnAllSourcesProcessed,
SourceDataOverride_OnAllSourcesProcessed,
UpdateGameThreadCopies,
Finished,
};
namespace Audio
{
class FMixerSubmix;
class FMixerDevice;
class FMixerSourceVoice;
class FMixerSourceBuffer;
class ISourceListener;
class FMixerSourceSubmixOutputBuffer;
class ISourceListener
{
public:
virtual ~ISourceListener() = default;
// Called before a source begins to generate audio.
virtual void OnBeginGenerate() = 0;
// Called when a loop point is hit
virtual void OnLoopEnd() = 0;
// Called when the source finishes on the audio render thread
virtual void OnDone() = 0;
// Called when the source's effect tails finish on the audio render thread.
virtual void OnEffectTailsDone() = 0;
};
struct FMixerSourceSubmixSend
{
// The submix ptr
FMixerSubmixWeakPtr Submix;
// The amount of audio that is to be mixed into this submix
float SendLevel = 0.0f;
// Whather or not this is the primary send (i.e. first in the send chain)
bool bIsMainSend = false;
// Whether or not this is a pre-distance attenuation send
EMixerSourceSubmixSendStage SubmixSendStage = EMixerSourceSubmixSendStage::PostDistanceAttenuation;
// If this is a soundfield submix, this is a pointer to the submix's Soundfield Factory.
// If this is nullptr, the submix is not a soundfield submix.
ISoundfieldFactory* SoundfieldFactory = nullptr;
};
// Struct holding mappings of bus ids (unique ids) to send level
struct FInitAudioBusSend
{
uint32 AudioBusId = INDEX_NONE;
float SendLevel = 0.0f;
int32 BusChannels = 0;
};
struct FMixerSourceVoiceInitParams
{
TSharedPtr<FMixerSourceBuffer, ESPMode::ThreadSafe> MixerSourceBuffer = nullptr;
ISourceListener* SourceListener = nullptr;
TArray<FMixerSourceSubmixSend> SubmixSends;
TArray<FInitAudioBusSend> AudioBusSends[(int32)EBusSendType::Count];
uint32 AudioBusId = INDEX_NONE;
int32 AudioBusChannels = 0;
float SourceBusDuration = 0.0f;
uint32 SourceEffectChainId = INDEX_NONE;
TArray<FSourceEffectChainEntry> SourceEffectChain;
int32 SourceEffectChainMaxSupportedChannels = 0;
FMixerSourceVoice* SourceVoice = nullptr;
int32 NumInputChannels = 0;
int32 NumInputFrames = 0;
float EnvelopeFollowerAttackTime = 10.0f;
float EnvelopeFollowerReleaseTime = 100.0f;
FString DebugName;
USpatializationPluginSourceSettingsBase* SpatializationPluginSettings = nullptr;
UOcclusionPluginSourceSettingsBase* OcclusionPluginSettings = nullptr;
UReverbPluginSourceSettingsBase* ReverbPluginSettings = nullptr;
USourceDataOverridePluginSourceSettingsBase* SourceDataOverridePluginSettings = nullptr;
FSoundModulationDefaultSettings ModulationSettings;
FQuartzQuantizedRequestData QuantizedRequestData;
FSharedISourceBufferListenerPtr SourceBufferListener;
IAudioLinkFactory::FAudioLinkSourcePushedSharedPtr AudioLink;
FName AudioComponentUserID;
uint64 AudioComponentID = 0;
bool bIs3D = false;
bool bPlayEffectChainTails = false;
bool bUseHRTFSpatialization = false;
bool bIsExternalSend = false;
bool bIsDebugMode = false;
bool bEnableBusSends = false;
bool bEnableBaseSubmix = false;
bool bEnableSubmixSends = false;
bool bIsVorbis = false;
bool bIsSoundfield = false;
bool bIsSeeking = false;
bool bShouldSourceBufferListenerZeroBuffer = false;
uint32 PlayOrder = INDEX_NONE;
uint32 ActiveSoundPlayOrder = INDEX_NONE;
};
struct FSourceManagerInitParams
{
// Total number of sources to use in the source manager
int32 NumSources = 0;
};
class FMixerSourceManager
{
public:
FMixerSourceManager(FMixerDevice* InMixerDevice);
~FMixerSourceManager();
void Init(const FSourceManagerInitParams& InitParams);
void Update(bool bTimedOut = false);
bool GetFreeSourceId(int32& OutSourceId);
int32 GetNumActiveSources() const;
int32 GetNumActiveAudioBuses() const;
void ReleaseSourceId(const int32 SourceId);
void InitSource(const int32 SourceId, const FMixerSourceVoiceInitParams& InitParams);
// Creates and starts an audio bus manually.
UE_DEPRECATED(5.6, "Use the StartAudioBus version that requires an AudioBus name.")
void StartAudioBus(FAudioBusKey InAudioBusKey, int32 InNumChannels, bool bInIsAutomatic);
void StartAudioBus(FAudioBusKey InAudioBusKey, const FString& InAudioBusName, int32 InNumChannels, bool bInIsAutomatic);
// Stops an audio bus manually
void StopAudioBus(FAudioBusKey InAudioBusKey);
// Queries if an audio bus is active. Must be called from the audio thread.
bool IsAudioBusActive(FAudioBusKey InAudioBusKey) const;
// Returns the number of channels currently set for the audio bus associated with
// the provided BusId. Returns 0 if the audio bus is inactive.
int32 GetAudioBusNumChannels(FAudioBusKey InAudioBusKey) const;
// Adds a patch output for an audio bus from the Audio Render Thread
void AddPatchOutputForAudioBus(FAudioBusKey InAudioBusKey, const FPatchOutputStrongPtr& InPatchOutputStrongPtr);
// Adds a patch output for an audio bus from the Audio Thread
void AddPatchOutputForAudioBus_AudioThread(FAudioBusKey InAudioBusKey, const FPatchOutputStrongPtr& InPatchOutputStrongPtr);
// Adds a patch input for an audio bus
void AddPatchInputForAudioBus(FAudioBusKey InAudioBusKey, const FPatchInput& InPatchInput);
// Adds a patch input for an audio bus from the Audio Thread
void AddPatchInputForAudioBus_AudioThread(FAudioBusKey InAudioBusKey, const FPatchInput& InPatchInput);
void Play(const int32 SourceId);
void Stop(const int32 SourceId);
void CancelQuantizedSound(const int32 SourceId);
void StopInternal(const int32 SourceId);
void StopFade(const int32 SourceId, const int32 NumFrames);
void Pause(const int32 SourceId);
void SetPitch(const int32 SourceId, const float Pitch);
void SetVolume(const int32 SourceId, const float Volume);
void SetDistanceAttenuation(const int32 SourceId, const float DistanceAttenuation);
void SetSpatializationParams(const int32 SourceId, const FSpatializationParams& InParams);
void SetChannelMap(const int32 SourceId, const uint32 NumInputChannels, const Audio::FAlignedFloatBuffer& InChannelMap, const bool bInIs3D, const bool bInIsCenterChannelOnly);
void SetLPFFrequency(const int32 SourceId, const float Frequency);
void SetHPFFrequency(const int32 SourceId, const float Frequency);
// Sets base (i.e. carrier) frequency of modulatable parameters
void SetModPitch(const int32 SourceId, const float InModPitch);
void SetModVolume(const int32 SourceId, const float InModVolume);
void SetModLPFFrequency(const int32 SourceId, const float InModFrequency);
void SetModHPFFrequency(const int32 SourceId, const float InModFrequency);
void SetModulationRouting(const int32 SourceId, FSoundModulationDefaultSettings& ModulationSettings);
void SetSourceBufferListener(const int32 SourceId, FSharedISourceBufferListenerPtr& InSourceBufferListener, bool InShouldSourceBufferListenerZeroBuffer);
void SetListenerTransforms(const TArray<FTransform>& ListenerTransforms);
const TArray<FTransform>* GetListenerTransforms() const;
int64 GetNumFramesPlayed(const int32 SourceId) const;
float GetEnvelopeValue(const int32 SourceId) const;
float GetVolumeModulationValue(const int32 SourceId) const;
#if ENABLE_AUDIO_DEBUG
double GetCPUCoreUtilization(const int32 SourceId) const;
#endif // ENABLE_AUDIO_DEBUG
float GetRelativeRenderCost(const int32 SourceId) const;
bool IsUsingHRTFSpatializer(const int32 SourceId) const;
bool NeedsSpeakerMap(const int32 SourceId) const;
void ComputeNextBlockOfSamples();
void UpdateSourceState();
void ClearStoppingSounds();
void MixOutputBuffers(const int32 SourceId, int32 InNumOutputChannels, const float InSendLevel, EMixerSourceSubmixSendStage InSubmixSendStage, FAlignedFloatBuffer& OutWetBuffer) const;
// Retrieves a channel map for the given source ID for the given output channels
// can be used even when a source is 3D if the source is doing any kind of bus sending or otherwise needs a channel map
void Get2DChannelMap(const int32 SourceId, int32 InNumOutputChannels, Audio::FAlignedFloatBuffer& OutChannelMap);
// Called by a soundfield submix to get encoded audio.
// If this source wasn't encoded (possibly because it is paused or finished playing),
// this returns nullptr.
// Returned nonnull pointers are only guaranteed to be valid on the audio mixer render thread.
const ISoundfieldAudioPacket* GetEncodedOutput(const int32 SourceId, const FSoundfieldEncodingKey& InKey) const;
const FQuat GetListenerRotation(const int32 SourceId) const;
void SetSubmixSendInfo(const int32 SourceId, const FMixerSourceSubmixSend& SubmixSend);
void ClearSubmixSendInfo(const int32 SourceId, const FMixerSourceSubmixSend& SubmixSend);
void SetBusSendInfo(const int32 SourceId, EBusSendType InAudioBusSendType, uint32 AudiobusId, float BusSendLevel);
void UpdateDeviceChannelCount(const int32 InNumOutputChannels);
void UpdateSourceEffectChain(const uint32 SourceEffectChainId, const TArray<FSourceEffectChainEntry>& SourceEffectChain, const bool bPlayEffectChainTails);
// Quantized event methods
void PauseSoundForQuantizationCommand(const int32 SourceId);
void SetSubBufferDelayForSound(const int32 SourceId, const int32 FramesToDelay);
void UnPauseSoundForQuantizationCommand(const int32 SourceId);
// Buffer getters
const float* GetPreDistanceAttenuationBuffer(const int32 SourceId) const;
const float* GetPreEffectBuffer(const int32 SourceId) const;
const float* GetPreviousSourceBusBuffer(const int32 SourceId) const;
const float* GetPreviousAudioBusBuffer(const int32 AudioBusId) const;
int32 GetNumChannels(const int32 SourceId) const;
int32 GetNumOutputFrames() const { return NumOutputFrames; }
bool IsSourceBus(const int32 SourceId) const;
void PumpCommandQueue();
void UpdatePendingReleaseData(bool bForceWait = false);
void FlushCommandQueue(bool bPumpCommandQueue = false);
// Pushes a TFUnction command into an MPSC queue from an arbitrary thread to the audio render thread
void AudioMixerThreadMPSCCommand(TFunction<void()>&& InCommand, const char* InDebugString=nullptr);
void AddPendingAudioBusConnection(FAudioBusKey AudioBusKey, int32 NumChannels, bool bIsAutomatic, FPatchInput PatchInput)
{
PendingAudioBusConnections.Enqueue(FPendingAudioBusConnection{ FPendingAudioBusConnection::FPatchVariant(TInPlaceType<FPatchInput>(), MoveTemp(PatchInput)), MoveTemp(AudioBusKey), NumChannels, bIsAutomatic });
}
void AddPendingAudioBusConnection(FAudioBusKey AudioBusKey, int32 NumChannels, bool bIsAutomatic, FPatchOutputStrongPtr PatchOutputStrongPtr)
{
PendingAudioBusConnections.Enqueue(FPendingAudioBusConnection{ FPendingAudioBusConnection::FPatchVariant(TInPlaceType<FPatchOutputStrongPtr>(), MoveTemp(PatchOutputStrongPtr)), MoveTemp(AudioBusKey), NumChannels, bIsAutomatic });
}
private:
#define INVALID_AUDIO_RENDER_THREAD_ID static_cast<uint32>(-1)
uint32 AudioRenderThreadId = INVALID_AUDIO_RENDER_THREAD_ID;
void ReleaseSource(const int32 SourceId);
void BuildSourceEffectChain(const int32 SourceId, FSoundEffectSourceInitData& InitData, const TArray<FSourceEffectChainEntry>& SourceEffectChain, TArray<TSoundEffectSourcePtr>& OutSourceEffects);
void ResetSourceEffectChain(const int32 SourceId);
void ReadSourceFrame(const int32 SourceId);
void GenerateSourceAudio(const bool bGenerateBuses);
void GenerateSourceAudio(const bool bGenerateBuses, const int32 SourceIdStart, const int32 SourceIdEnd);
void ComputeSourceBuffer(const bool bGenerateBuses, const int32 SourceId);
void ComputePostSourceEffectBuffer(const bool bGenerateBuses, const int32 SourceId);
void ComputeOutputBuffers(const bool bGenerateBuses, const int32 SourceId);
void ConnectBusPatches();
void ComputeBuses();
void UpdateBuses();
float GetFloatCompareTolerance() const;
float GetCommandQueueFillPercentage() const;
struct FAudioMixerThreadCommand
{
// ctor
FAudioMixerThreadCommand() = default;
FAudioMixerThreadCommand(TFunction<void()>&& InFunction, const char* InDebugString, bool bInDeferExecution = false);
// function-call operator
void operator()() const;
// data
TFunction<void()> Function;
// Defers the execution by a single call to PumpCommandQueue()
// (used for commands that affect a playing source,
// and that source gets initialized after the command executes
bool bDeferExecution = false;
#if WITH_AUDIO_MIXER_THREAD_COMMAND_DEBUG
const char* DebugString=nullptr; // Statically defined string from macro AUDIO_MIXER_THREAD_COMMAND_STRING
mutable uint64_t StartExecuteTimeInCycles=0; // Set just before Function is called, for diagnostics.
#endif // #if WITH_AUDIO_MIXER_THREAD_COMMAND_DEBUG
FString GetSafeDebugString() const;
float GetExecuteTimeInSeconds() const;
};
void AudioMixerThreadCommand(TFunction<void()>&& InFunction, const char* InDebugString = nullptr, bool bInDeferExecution = false);
// Critical section to ensure mutating effect chains is thread-safe
FCriticalSection EffectChainMutationCriticalSection;
FMixerDevice* MixerDevice;
// Info about spatialization plugin
FAudioDevice::FAudioSpatializationInterfaceInfo SpatialInterfaceInfo;
// Cached ptr to an optional source data override plugin
TAudioSourceDataOverridePtr SourceDataOverridePlugin;
IAudioLinkFactory* AudioLinkFactory = nullptr;
// Array of pointers to game thread audio source objects
TArray<FMixerSourceVoice*> MixerSources;
// A command queue to execute commands from audio thread (or game thread) to audio mixer device thread.
struct FCommands
{
FThreadSafeCounter NumTimesOvergrown = 0;
TArray<FAudioMixerThreadCommand> SourceCommandQueue;
};
FCommands CommandBuffers[2];
FThreadSafeCounter RenderThreadCommandBufferIndex;
FEvent* CommandsProcessedEvent;
FCriticalSection CommandBufferIndexCriticalSection;
TArray<int32> DebugSoloSources;
using FAudioMixerMpscCommand = FAudioMixerThreadCommand;
TMpscQueue<FAudioMixerMpscCommand> MpscCommandQueue;
struct FSourceInfo
{
FSourceInfo() : Resampler(1 /* NumChannels */) {}
~FSourceInfo() {}
// Object which handles source buffer decoding
TSharedPtr<FMixerSourceBuffer, ESPMode::ThreadSafe> MixerSourceBuffer;
ISourceListener* SourceListener;
// Data used for rendering sources
TSharedPtr<Audio::FAlignedFloatBuffer, ESPMode::ThreadSafe> CurrentPCMBuffer;
// The post-attenuation source buffer, used to send audio to submixes
Audio::FAlignedFloatBuffer SourceBuffer;
Audio::FAlignedFloatBuffer PreEffectBuffer;
Audio::FAlignedFloatBuffer PreDistanceAttenuationBuffer;
Audio::FAlignedFloatBuffer SourceEffectScratchBuffer;
// Data used for delaying the rendering of source audio for sample-accurate quantization
int32 SubCallbackDelayLengthInFrames{ 0 };
Audio::TCircularAudioBuffer<float> SourceBufferDelayLine;
TArray<float> CurrentFrameValues;
TArray<float> NextFrameValues;
float CurrentFrameAlpha;
int32 CurrentFrameIndex;
int64 NumFramesPlayed;
TArray<FMixerSourceSubmixSend> SubmixSends;
// What audio bus Id this source is sonfiying, if it is a source bus. This is INDEX_NONE for sources which are not source buses.
uint32 AudioBusId;
// Number of samples to count for source bus
int64 SourceBusDurationFrames;
// What buses this source is sending its audio to. Used to remove this source from the bus send list.
TArray<uint32> AudioBusSends[(int32)EBusSendType::Count];
// Interpolated source params
FParam PitchSourceParam;
float VolumeSourceStart;
float VolumeSourceDestination;
float VolumeFadeSlope;
float VolumeFadeStart;
int32 VolumeFadeFramePosition;
int32 VolumeFadeNumFrames;
float DistanceAttenuationSourceStart;
float DistanceAttenuationSourceDestination;
// Legacy filter LFP & HPF frequency set directly (not by modulation) on source
float LowPassFreq;
float HighPassFreq;
// One-Pole LPFs and HPFs per source
Audio::FInterpolatedLPF LowPassFilter;
Audio::FInterpolatedHPF HighPassFilter;
// Source effect instances
uint32 SourceEffectChainId;
TArray<TSoundEffectSourcePtr> SourceEffects;
TArray<USoundEffectSourcePreset*> SourceEffectPresets;
bool bEffectTailsDone;
FSoundEffectSourceInputData SourceEffectInputData;
FAudioPluginSourceOutputData AudioPluginOutputData;
// A DSP object which tracks the amplitude envelope of a source.
Audio::FInlineEnvelopeFollower SourceEnvelopeFollower;
float SourceEnvelopeValue;
// Modulation destinations
Audio::FModulationDestination VolumeModulation;
Audio::FModulationDestination PitchModulation;
Audio::FModulationDestination LowpassModulation;
Audio::FModulationDestination HighpassModulation;
// Modulation Base (i.e. Carrier) Values
float VolumeModulationBase;
float PitchModulationBase;
float LowpassModulationBase;
float HighpassModulationBase;
FSpatializationParams SpatParams;
Audio::FAlignedFloatBuffer ScratchChannelMap;
// Quantization data
FQuartzQuantizedCommandHandle QuantizedCommandHandle;
// Optional Source buffer listener.
FSharedISourceBufferListenerPtr SourceBufferListener;
// Optional AudioLink.
IAudioLinkFactory::FAudioLinkSourcePushedSharedPtr AudioLink;
// State management
uint8 bIs3D:1;
uint8 bIsCenterChannelOnly:1;
uint8 bIsActive:1;
uint8 bIsPlaying:1;
uint8 bIsPaused:1;
uint8 bIsPausedForQuantization:1;
uint8 bDelayLineSet:1;
uint8 bIsStopping:1;
uint8 bHasStarted:1;
uint8 bIsBusy:1;
uint8 bUseHRTFSpatializer:1;
uint8 bIsExternalSend:1;
uint8 bUseOcclusionPlugin:1;
uint8 bUseReverbPlugin:1;
uint8 bIsDone:1;
uint8 bIsLastBuffer:1;
uint8 bEnableBusSends : 1;
uint8 bEnableBaseSubmix : 1;
uint8 bEnableSubmixSends : 1;
uint8 bIsVorbis:1;
uint8 bIsSoundfield:1;
uint8 bHasPreDistanceAttenuationSend:1;
uint8 bModFiltersUpdated : 1;
uint8 bShouldSourceBufferListenerZeroBuffer : 1;
uint8 bResampling : 1;
// Source format info
int32 NumInputChannels;
int32 NumPostEffectChannels;
int32 NumInputFrames;
uint32 PlayOrder;
uint32 ActiveSoundPlayOrder;
// ID for associated Audio Component if there is one, 0 otherwise
uint64 AudioComponentID;
// Resampler instance
FRuntimeResampler Resampler;
FORCEINLINE void ResetModulators(const Audio::FDeviceId InDeviceId)
{
VolumeModulation.Init(InDeviceId, FName("Volume"), false /* bInIsBuffered */, true /* bInValueLinear */);
PitchModulation.Init(InDeviceId, FName("Pitch"));
HighpassModulation.Init(InDeviceId, FName("HPFCutoffFrequency"));
LowpassModulation.Init(InDeviceId, FName("LPFCutoffFrequency"));
VolumeModulationBase = 0.0f;
PitchModulationBase = 0.0f;
HighpassModulationBase = MIN_FILTER_FREQUENCY;
LowpassModulationBase = MAX_FILTER_FREQUENCY;
}
//Helper function for determining if OutputToBusOnly is enabled
bool IsRenderingToSubmixes() const;
#if AUDIO_MIXER_ENABLE_DEBUG_MODE
uint8 bIsDebugMode : 1;
FString DebugName;
#endif // AUDIO_MIXER_ENABLE_DEBUG_MODE
FString GetDebugName() const
{
#if AUDIO_MIXER_ENABLE_DEBUG_MODE
return DebugName;
#else
return {};
#endif //AUDIO_MIXER_ENABLE_DEBUG_MODE
}
// return the number of sample frames in the current input PCM buffer (if any)
int32 GetCurrentAudioChunkNumFrames() const
{
if (CurrentPCMBuffer)
{
check(NumInputChannels != 0);
return CurrentPCMBuffer->Num() / NumInputChannels;
}
else
{
return 0;
}
}
};
static void ApplyDistanceAttenuation(FSourceInfo& InSourceInfo, int32 NumSamples);
void ComputePluginAudio(FSourceInfo& InSourceInfo, FMixerSourceSubmixOutputBuffer& InSourceSubmixOutputBuffer, int32 SourceId, int32 NumSamples);
// Hang/crash diagnostics.
void DoStallDiagnostics();
void LogRenderThreadStall();
void LogInflightAsyncTasks();
void LogCallstacks();
void LogCallstack(uint32 InThreadId);
// Array of listener transforms
TArray<FTransform> ListenerTransforms;
// Array of source infos.
TArray<FSourceInfo> SourceInfos;
// This array is independent of SourceInfos array to optimize for cache coherency
TArray<FMixerSourceSubmixOutputBuffer> SourceSubmixOutputBuffers;
// Map of bus object Id's to audio bus data.
TMap<FAudioBusKey, TSharedPtr<FMixerAudioBus>> AudioBuses;
TArray<FAudioBusKey> AudioBusKeys_AudioThread;
// Array of task data waiting to finished. Processed on audio render thread.
TArray<TSharedPtr<FMixerSourceBuffer, ESPMode::ThreadSafe>> PendingSourceBuffers;
// General information about sources in source manager accessible from game thread
struct FGameThreadInfo
{
TArray<int32> FreeSourceIndices;
TArray<bool> bIsBusy;
TArray<bool> bNeedsSpeakerMap;
TArray<bool> bIsDebugMode;
TArray<bool> bIsUsingHRTFSpatializer;
#if ENABLE_AUDIO_DEBUG
TArray<double> CPUCoreUtilization;
#endif // #if ENABLE_AUDIO_DEBUG
TArray<float> RelativeRenderCost;
TArray<float> ModulationVolume;
} GameThreadInfo;
int32 NumActiveSources;
int32 NumTotalSources;
int32 NumOutputFrames;
int32 NumOutputSamples;
// Commands queued up to execute
FThreadSafeCounter NumCommands;
uint8 bInitialized : 1;
uint8 bUsingSpatializationPlugin : 1;
uint8 bUsingSourceDataOverridePlugin : 1;
// Set to true when the audio source manager should pump the command queue
FThreadSafeBool bPumpQueue;
std::atomic<uint64> LastPumpCompleteTimeInCycles=0;
std::atomic<ESourceManagerRenderThreadPhase> RenderThreadPhase=ESourceManagerRenderThreadPhase::Begin;
FRWLock CurrentlyExecutingCmdLock; // R/W slim lock for the currently executing cmd, so we can safely query it.
FAudioMixerThreadCommand CurrentlyExecuteingCmd; // Keep this as a member so we can't always peek the executing cmd.
struct FPendingAudioBusConnection
{
using FPatchVariant = TVariant<FPatchInput, FPatchOutputStrongPtr>;
FPatchVariant PatchVariant;
FAudioBusKey AudioBusKey;
int32 NumChannels = 0;
bool bIsAutomatic = false;
};
TMpscQueue<FPendingAudioBusConnection> PendingAudioBusConnections;
friend class FMixerSourceVoice;
};
}