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

696 lines
27 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "AudioBusSubsystem.h"
#include "AudioMixer.h"
#include "AudioDefines.h"
#include "CoreMinimal.h"
#include "SampleBuffer.h"
#include "IAudioEndpoint.h"
#include "ISoundfieldEndpoint.h"
#include "Sound/SoundSubmix.h"
#include "Sound/SoundModulationDestination.h"
#include "DSP/EnvelopeFollower.h"
#include "DSP/MultithreadedPatching.h"
#include "DSP/SpectrumAnalyzer.h"
#include "Templates/SharedPointer.h"
#include "AudioDynamicParameter.h"
#include "Stats/Stats.h"
#include "UObject/WeakObjectPtrTemplates.h"
#include "IAudioLinkFactory.h"
// The time it takes to process the submix graph. Process submix effects, mix into the submix buffer, etc.
DECLARE_CYCLE_STAT_EXTERN(TEXT("Submix Graph"), STAT_AudioMixerSubmixes, STATGROUP_AudioMixer, AUDIOMIXER_API);
// The time it takes to process the endpoint submixes.
DECLARE_CYCLE_STAT_EXTERN(TEXT("Submix Graph Endpoint"), STAT_AudioMixerEndpointSubmixes, STATGROUP_AudioMixer, AUDIOMIXER_API);
// The time it takes to process the submix graph. Process submix effects, mix into the submix buffer, etc.
DECLARE_CYCLE_STAT_EXTERN(TEXT("Submix Graph Child Processing"), STAT_AudioMixerSubmixChildren, STATGROUP_AudioMixer, AUDIOMIXER_API);
// The time it takes to process the submix graph. Process submix effects, mix into the submix buffer, etc.
DECLARE_CYCLE_STAT_EXTERN(TEXT("Submix Graph Source Mixing"), STAT_AudioMixerSubmixSource, STATGROUP_AudioMixer, AUDIOMIXER_API);
// The time it takes to process the submix graph. Process submix effects, mix into the submix buffer, etc.
DECLARE_CYCLE_STAT_EXTERN(TEXT("Submix Graph Effect Processing"), STAT_AudioMixerSubmixEffectProcessing, STATGROUP_AudioMixer, AUDIOMIXER_API);
// The time it takes to process the submix buffer listeners.
DECLARE_CYCLE_STAT_WITH_FLAGS_EXTERN(TEXT("Submix Buffer Listeners"), STAT_AudioMixerSubmixBufferListeners, STATGROUP_AudioMixer, EStatFlags::Verbose, AUDIOMIXER_API);
// The time it takes to process the submix soundfield child submixes.
DECLARE_CYCLE_STAT_EXTERN(TEXT("Submix Soundfield Children"), STAT_AudioMixerSubmixSoundfieldChildren, STATGROUP_AudioMixer, AUDIOMIXER_API);
// The time it takes to process the submix soundfield sources.
DECLARE_CYCLE_STAT_EXTERN(TEXT("Submix Soundfield Sources"), STAT_AudioMixerSubmixSoundfieldSources, STATGROUP_AudioMixer, AUDIOMIXER_API);
// The time it takes to process the submix soundfield processors..
DECLARE_CYCLE_STAT_EXTERN(TEXT("Submix Soundfield Processors"), STAT_AudioMixerSubmixSoundfieldProcessors, STATGROUP_AudioMixer, AUDIOMIXER_API);
// Forward Declarations
class FOnSubmixEnvelopeBP;
class USoundEffectSubmix;
class USoundSubmix;
class USoundSubmixBase;
class USoundModulatorBase;
namespace Audio
{
class IAudioMixerEffect;
class FMixerSourceVoice;
class FMixerDevice;
enum EMixerSourceSubmixSendStage
{
// Whether to do the send pre distance attenuation
PostDistanceAttenuation,
// Whether to do the send post distance attenuation
PreDistanceAttenuation,
};
struct FSubmixVoiceData
{
float SendLevel;
EMixerSourceSubmixSendStage SubmixSendStage;
FSubmixVoiceData()
: SendLevel(1.0f)
, SubmixSendStage(EMixerSourceSubmixSendStage::PostDistanceAttenuation)
{
}
};
class FMixerSubmix;
struct FChildSubmixInfo : FNoncopyable
{
TWeakPtr<FMixerSubmix, ESPMode::ThreadSafe> SubmixPtr;
// If the child submix is not a soundfield submix, we may need to encode its audio output in ProcessAudio.
TUniquePtr<ISoundfieldEncoderStream> Encoder;
// If this child submix is a soundfield submix that we can read the output of, we may need to transcode it's audio output.
TUniquePtr<ISoundfieldTranscodeStream> Transcoder;
// This is filled by either the Encoder or the Transcoder, and passed to this submix' mixer.
TUniquePtr<ISoundfieldAudioPacket> IncomingPacketToTranscode;
FChildSubmixInfo()
{}
FChildSubmixInfo(TWeakPtr<FMixerSubmix, ESPMode::ThreadSafe> SubmixWeakPtr)
: SubmixPtr(SubmixWeakPtr)
{
}
};
class FMixerSubmix
{
public:
AUDIOMIXER_API FMixerSubmix(FMixerDevice* InMixerDevice);
AUDIOMIXER_API virtual ~FMixerSubmix();
// Initialize the submix object with the USoundSubmix ptr. Sets up child and parent connects.
AUDIOMIXER_API void Init(const USoundSubmixBase* InSoundSubmix, bool bAllowReInit = true);
// Returns the mixer submix Id
uint32 GetId() const { return Id; }
// Return the owners name
const FString& GetName() const { return SubmixName; }
// Sets the parent submix to the given submix
AUDIOMIXER_API void SetParentSubmix(TWeakPtr<FMixerSubmix, ESPMode::ThreadSafe> Submix);
// Adds the given submix to this submix's children
AUDIOMIXER_API void AddChildSubmix(TWeakPtr<FMixerSubmix, ESPMode::ThreadSafe> Submix);
// Removes the given submix from this submix's children
AUDIOMIXER_API void RemoveChildSubmix(TWeakPtr<FMixerSubmix, ESPMode::ThreadSafe> SubmixWeakPtr);
// Registers the given audiobus to this submix
AUDIOMIXER_API void RegisterAudioBus(const Audio::FAudioBusKey& InAudioBusKey, Audio::FPatchInput&& InPatchInput);
// Unregisters a registered audiobus from this submix (if any)
AUDIOMIXER_API void UnregisterAudioBus(const Audio::FAudioBusKey& InAudioBusKey);
// Sets the output level of the submix in linear gain
AUDIOMIXER_API void SetOutputVolume(float InOutputLevel);
// Sets the static output volume of the submix in linear gain
AUDIOMIXER_API void SetDryLevel(float InDryLevel);
// Sets the wet level of the submix in linear gain
AUDIOMIXER_API void SetWetLevel(float InWetLevel);
// Update modulation settings of the submix
AUDIOMIXER_API void UpdateModulationSettings(const TSet<TObjectPtr<USoundModulatorBase>>& InOutputModulators, const TSet<TObjectPtr<USoundModulatorBase>>& InWetLevelModulators, const TSet<TObjectPtr<USoundModulatorBase>>& InDryLevelModulators);
// Update modulation settings of the submix with Decibel values
AUDIOMIXER_API void SetModulationBaseLevels(float InVolumeModBaseDb, float InWetModeBaseDb, float InDryModBaseDb);
FModulationDestination* GetOutputVolumeDestination();
FModulationDestination* GetWetVolumeDestination();
// Gets the submix channels channels
AUDIOMIXER_API int32 GetSubmixChannels() const;
// Gets this submix's parent submix
AUDIOMIXER_API TWeakPtr<FMixerSubmix, ESPMode::ThreadSafe> GetParentSubmix();
// Returns the number of source voices currently a part of this submix.
AUDIOMIXER_API int32 GetNumSourceVoices() const;
// Returns the number of wet effects in this submix.
AUDIOMIXER_API int32 GetNumEffects() const;
// Returns the size of the submix chain.
AUDIOMIXER_API int32 GetSizeOfSubmixChain() const;
// Add (if not already added) or sets the amount of the source voice's send amount
AUDIOMIXER_API void AddOrSetSourceVoice(FMixerSourceVoice* InSourceVoice, const float SendLevel, EMixerSourceSubmixSendStage InSubmixSendStage);
AUDIOMIXER_API FPatchOutputStrongPtr AddPatch(float InGain);
/** Removes the given source voice from the submix. */
AUDIOMIXER_API void RemoveSourceVoice(FMixerSourceVoice* InSourceVoice);
/** Appends the effect submix to the effect submix chain. */
AUDIOMIXER_API void AddSoundEffectSubmix(FSoundEffectSubmixPtr InSoundEffectSubmix);
/** Removes the submix effect from the effect submix chain. */
AUDIOMIXER_API void RemoveSoundEffectSubmix(uint32 SubmixPresetId);
/** Removes the submix effect from the effect submix chain at the given submix index. */
AUDIOMIXER_API void RemoveSoundEffectSubmixAtIndex(int32 InIndex);
/** Clears all submix effects from the effect submix chain. */
AUDIOMIXER_API void ClearSoundEffectSubmixes();
/** Sets a submix effect chain override with the given fade time in seconds. */
AUDIOMIXER_API void SetSubmixEffectChainOverride(const TArray<FSoundEffectSubmixPtr>& InSubmixEffectPresetChain, float InFadeTimeSec);
/** Clears any submix effect chain overrides in the given fade time in seconds. */
AUDIOMIXER_API void ClearSubmixEffectChainOverride(float InFadeTimeSec);
/** Swaps effect for provided submix at the given index. Fails if effect at index doesn't exist */
AUDIOMIXER_API void ReplaceSoundEffectSubmix(int32 InIndex, FSoundEffectSubmixPtr InEffectInstance);
/** Whether or not this submix instance is muted. */
AUDIOMIXER_API void SetBackgroundMuted(bool bInMuted);
/** Checks to see if submix is valid. Submix can be considered invalid if the OwningSubmix
* pointer is stale.
*/
AUDIOMIXER_API bool IsValid() const;
// Function which processes audio.
AUDIOMIXER_API void ProcessAudio(FAlignedFloatBuffer& OutAudio);
AUDIOMIXER_API void ProcessAudio(ISoundfieldAudioPacket& OutputAudio);
AUDIOMIXER_API void SendAudioToSubmixBufferListeners(FAlignedFloatBuffer& OutAudioBuffer);
// This should be called if this submix doesn't send it's audio to a parent submix,
// but rather an external endpoint.
AUDIOMIXER_API void ProcessAudioAndSendToEndpoint();
// Returns the device sample rate this submix is rendering to
AUDIOMIXER_API int32 GetSampleRate() const;
// Returns the output channels this submix is rendering to
AUDIOMIXER_API int32 GetNumOutputChannels() const;
// Returns the number of effects in this submix's effect chain
AUDIOMIXER_API int32 GetNumChainEffects();
// Returns the submix effect at the given effect chain index
AUDIOMIXER_API FSoundEffectSubmixPtr GetSubmixEffect(const int32 InIndex);
// This must be called on the entire submix graph before calling SetupSoundfieldStreams.
AUDIOMIXER_API void SetSoundfieldFactory(ISoundfieldFactory* InSoundfieldFactory);
// updates settings, potentially creating or removing ambisonics streams based on what types of submixes this submix is connected to.
AUDIOMIXER_API void SetupSoundfieldStreams(const USoundfieldEncodingSettingsBase* SoundfieldSettings, TArray<USoundfieldEffectBase*>& Processors, ISoundfieldFactory* InSoundfieldFactory);
AUDIOMIXER_API void TeardownSoundfieldStreams();
AUDIOMIXER_API void SetupEndpoint(IAudioEndpointFactory* InFactory, const UAudioEndpointSettingsBase* InSettings);
AUDIOMIXER_API void SetupEndpoint(ISoundfieldEndpointFactory* InFactory, const USoundfieldEndpointSettingsBase* InSettings);
AUDIOMIXER_API void UpdateEndpointSettings(TUniquePtr<IAudioEndpointSettingsProxy>&& InSettings);
AUDIOMIXER_API void UpdateEndpointSettings(TUniquePtr<ISoundfieldEndpointSettingsProxy>&& InSettings);
// This is called by the corresponding USoundSubmix when StartRecordingOutput is called.
AUDIOMIXER_API void OnStartRecordingOutput(float ExpectedDuration);
// This is called by the corresponding USoundSubmix when StopRecordingOutput is called.
AUDIOMIXER_API FAlignedFloatBuffer& OnStopRecordingOutput(float& OutNumChannels, float& OutSampleRate);
// This is called by the corresponding USoundSubmix when PauseRecording is called.
AUDIOMIXER_API void PauseRecordingOutput();
// This is called by the corresponding USoundSubmix when ResumeRecording is called.
AUDIOMIXER_API void ResumeRecordingOutput();
// Register buffer listener with this submix
// Unregister buffer listener with this submix
UE_DEPRECATED(5.4, "This function is deprecated. Use RegisterBufferListener version that is provided a shared reference to a listener.")
AUDIOMIXER_API void RegisterBufferListener(ISubmixBufferListener* BufferListener);
UE_DEPRECATED(5.4, "This function is deprecated. Use UnregisterBufferListener version that is provided a shared reference to a listener.")
AUDIOMIXER_API void UnregisterBufferListener(ISubmixBufferListener* BufferListener);
AUDIOMIXER_API void RegisterBufferListener(TSharedRef<ISubmixBufferListener, ESPMode::ThreadSafe> BufferListener);
AUDIOMIXER_API void UnregisterBufferListener(TSharedRef<ISubmixBufferListener, ESPMode::ThreadSafe> BufferListener);
// Starts envelope following with the given attack time and release time
AUDIOMIXER_API void StartEnvelopeFollowing(int32 AttackTime, int32 ReleaseTime);
// Stops envelope following the submix
AUDIOMIXER_API void StopEnvelopeFollowing();
// Adds an envelope follower delegate
AUDIOMIXER_API void AddEnvelopeFollowerDelegate(const FOnSubmixEnvelopeBP& OnSubmixEnvelopeBP);
// Removes an existing envelope follower delegate
AUDIOMIXER_API void RemoveEnvelopeFollowerDelegate(const FOnSubmixEnvelopeBP& OnSubmixEnvelopeBP);
// Initializes a new FFT analyzer for this submix and immediately begins feeding audio to it.
AUDIOMIXER_API void StartSpectrumAnalysis(const FSoundSpectrumAnalyzerSettings& InSettings);
// Terminates whatever FFT Analyzer is being used for this submix.
AUDIOMIXER_API void StopSpectrumAnalysis();
// Adds an spectral analysis delegate
AUDIOMIXER_API void AddSpectralAnalysisDelegate(const FSoundSpectrumAnalyzerDelegateSettings& InDelegateSettings, const FOnSubmixSpectralAnalysisBP& OnSubmixSpectralAnalysisBP);
// Removes an existing spectral analysis delegate
AUDIOMIXER_API void RemoveSpectralAnalysisDelegate(const FOnSubmixSpectralAnalysisBP& OnSubmixSpectralAnalysisBP);
// Gets the most recent magnitude values for each corresponding value in InFrequencies (in Hz).
// This requires StartSpectrumAnalysis to be called first.
AUDIOMIXER_API void GetMagnitudeForFrequencies(const TArray<float>& InFrequencies, TArray<float>& OutMagnitudes);
// Gets the most recent phase values for each corresponding value in InFrequencies (in Hz).
// This requires StartSpectrumAnalysis to be called first.
AUDIOMIXER_API void GetPhaseForFrequencies(const TArray<float>& InFrequencies, TArray<float>& OutPhases);
// Broadcast the envelope and submix delegates on the game thread
AUDIOMIXER_API void BroadcastDelegates();
// returns true if this submix is encoded to a soundfield.
AUDIOMIXER_API bool IsSoundfieldSubmix() const;
// returns true if this submix sends it's audio to the default endpoint.
AUDIOMIXER_API bool IsDefaultEndpointSubmix() const;
// Returns true if this submix sends its audio to an IAudioEndpoint.
AUDIOMIXER_API bool IsExternalEndpointSubmix() const;
// returns true if this submix sends its audio to an ISoundfieldEndpoint.
AUDIOMIXER_API bool IsSoundfieldEndpointSubmix() const;
//Returns true if this is an endpoint type that should no-op for this platform
AUDIOMIXER_API bool IsDummyEndpointSubmix() const;
// Returns true if the submix is currently rendering audio. The current rendering time is passed in.
AUDIOMIXER_API bool IsRenderingAudio() const;
// Set whether or not this submix is told to auto disable.
AUDIOMIXER_API void SetAutoDisable(bool bInAutoDisable);
// Sets the auto-disable time
AUDIOMIXER_API void SetAutoDisableTime(float InAutoDisableTime);
// Get a unique key for this submix's format and settings.
// If another submix has an identical format and settings it will have an equivalent key.
AUDIOMIXER_API FSoundfieldEncodingKey GetKeyForSubmixEncoding();
AUDIOMIXER_API ISoundfieldFactory* GetSoundfieldFactory();
AUDIOMIXER_API ISoundfieldEncodingSettingsProxy& GetSoundfieldSettings();
AUDIOMIXER_API FAudioPluginInitializationParams GetInitializationParamsForSoundfieldStream();
AUDIOMIXER_API FSoundfieldSpeakerPositionalData GetDefaultPositionalDataForAudioDevice();
TWeakPtr<FMixerSubmix, ESPMode::ThreadSafe> GetParent() const { return ParentSubmix; }
const TMap<uint32, FChildSubmixInfo>& GetChildren() const { return ChildSubmixes; }
protected:
// Initialize the submix internal
AUDIOMIXER_API void InitInternal();
// Down mix the given buffer to the desired down mix channel count
static AUDIOMIXER_API void DownmixBuffer(const int32 InChannels, const FAlignedFloatBuffer& InBuffer, const int32 OutChannels, FAlignedFloatBuffer& OutNewBuffer);
AUDIOMIXER_API void MixBufferDownToMono(const FAlignedFloatBuffer& InBuffer, int32 NumInputChannels, FAlignedFloatBuffer& OutBuffer);
AUDIOMIXER_API void SetupSoundfieldEncodersForChildren();
AUDIOMIXER_API void SetupSoundfieldEncodingForChild(FChildSubmixInfo& InChild);
// Check to see if we need to decode from ambisonics for parent
AUDIOMIXER_API void SetupSoundfieldStreamForParent();
// This sets up the ambisonics positional data for speakers, based on what new format we need to convert to.
AUDIOMIXER_API void SetUpSoundfieldPositionalData(const TSharedPtr<Audio::FMixerSubmix, ESPMode::ThreadSafe>& InParentSubmix);
// Encode a source and sum it into the mixed soundfield.
AUDIOMIXER_API void MixInSource(const ISoundfieldAudioPacket& InAudio, const ISoundfieldEncodingSettingsProxy& InSettings, ISoundfieldAudioPacket& PacketToSumTo);
AUDIOMIXER_API void UpdateListenerRotation(const FQuat& InRotation);
// Calls ProcessAudio on the child submix, performs all necessary conversions and mixes in it's resulting audio.
AUDIOMIXER_API void MixInChildSubmix(FChildSubmixInfo& Child, ISoundfieldAudioPacket& PacketToSumTo);
AUDIOMIXER_API FName GetSoundfieldFormat() const;
AUDIOMIXER_API TUniquePtr<ISoundfieldTranscodeStream> GetTranscoderForChildSubmix(const TSharedPtr<Audio::FMixerSubmix, ESPMode::ThreadSafe>& InChildSubmix);
protected:
struct FSubmixBufferListenerInfo
{
ISubmixBufferListener* Listener = nullptr;
FString Descriptor;
};
// Pump command queue
AUDIOMIXER_API void PumpCommandQueue();
// Add command to the command queue
AUDIOMIXER_API void SubmixCommand(TFunction<void()> Command);
// Generates audio from the given effect chain into the given buffer
AUDIOMIXER_API bool GenerateEffectChainAudio(FSoundEffectSubmixInputData& InputData, const FAlignedFloatBuffer& InAudioBuffer, TArray<FSoundEffectSubmixPtr>& InEffectChain, FAlignedFloatBuffer& OutBuffer);
// The name of this submix (the owning USoundSubmix) (at top so we can see in debugger it's name)
FString SubmixName;
// This mixer submix's Id
uint32 Id;
// Parent submix.
TWeakPtr<FMixerSubmix, ESPMode::ThreadSafe> ParentSubmix;
// Child submixes
TMap<uint32, FChildSubmixInfo> ChildSubmixes;
// Struct to hold record keeping data about effect chain overrides
struct FSubmixEffectFadeInfo
{
TArray<FSoundEffectSubmixPtr> EffectChain;
FDynamicParameter FadeVolume = FDynamicParameter(1.0f);
// If true, this effect override will be fading in or all the way faded in
bool bIsCurrentChain = false;
// If this effect fade info is the base effect
bool bIsBaseEffect = false;
};
// The array of submix effect overrides. There may be more than one if multiple are fading out. There should be only one fading in (the current override).
TArray<FSubmixEffectFadeInfo> EffectChains;
FAlignedFloatBuffer EffectChainOutputBuffer;
// Owning mixer device.
FMixerDevice* MixerDevice;
// Map of mixer source voices with a given send level for this submix
TMap<FMixerSourceVoice*, FSubmixVoiceData> MixerSourceVoices;
FAlignedFloatBuffer ScratchBuffer;
FAlignedFloatBuffer SubmixChainMixBuffer;
FAlignedFloatBuffer InputBuffer;
FAlignedFloatBuffer DownmixedBuffer;
FAlignedFloatBuffer SourceInputBuffer;
int32 NumChannels;
int32 NumSamples;
/**
* Individual processor in our
*/
struct FSoundfieldEffectProcessorData
{
TUniquePtr<ISoundfieldEffectSettingsProxy> Settings;
TUniquePtr<ISoundfieldEffectInstance> Processor;
FSoundfieldEffectProcessorData(ISoundfieldFactory* InFactory, ISoundfieldEncodingSettingsProxy& InSettings, USoundfieldEffectBase* InProcessorBase)
{
check(InFactory);
// As a sanity check, make sure if we've gotten to this point, this DSP processor supports this submix's format.
check(InProcessorBase->SupportsFormat(InFactory->GetSoundfieldFormatName()));
Processor = InProcessorBase->PrivateGetNewProcessor(InSettings);
// If the processor doesn't have any settings, get the default settings for a processor of this type.
const USoundfieldEffectSettingsBase* ProcessorSettings = InProcessorBase->Settings;
if (!ProcessorSettings)
{
ProcessorSettings = InProcessorBase->PrivateGetDefaultSettings();
}
Settings = ProcessorSettings->PrivateGetProxy();
}
};
struct FSoundfieldStreams
{
ISoundfieldFactory* Factory;
// This encoder is used for the mixed down audio from all non-soundfield submixes plugged into
// this submix. Will not be set up if ISoundfieldFactory::ShouldEncodeAllStreamsIndependently
// returns true.
TUniquePtr<ISoundfieldEncoderStream> DownmixedChildrenEncoder;
// Encoder used if a normal submix outputs to this submix.
TUniquePtr<ISoundfieldDecoderStream> ParentDecoder;
// This is the positional data we are decoding
FSoundfieldSpeakerPositionalData CachedPositionalData;
// Mixes all encoded child submix inputs.
TUniquePtr<ISoundfieldMixerStream> Mixer;
// This is the packet we mix all input sources and child submixes to.
TUniquePtr<ISoundfieldAudioPacket> MixedDownAudio;
// Current settings for this submix.
TUniquePtr<ISoundfieldEncodingSettingsProxy> Settings;
// All soundfield processors attached to this submix.
TArray<FSoundfieldEffectProcessorData> EffectProcessors;
// This critical section is contended by the soundfield overload of ProcessAudio and SetupSoundfieldStreams.
FCriticalSection StreamsLock;
FSoundfieldStreams()
: Factory(nullptr)
{}
void Reset()
{
Factory = nullptr;
ParentDecoder.Reset();
Mixer.Reset();
Settings.Reset();
}
};
FSoundfieldStreams SoundfieldStreams;
struct FEndpointData
{
// For endpoint submixes,
// this is the primary method of pushing audio to the endpoint.
Audio::FPatchInput Input;
TUniquePtr<IAudioEndpoint> NonSoundfieldEndpoint;
TUniquePtr<ISoundfieldEndpoint> SoundfieldEndpoint;
// for non-soundfield endpoints, we use these buffers for processing.
FAlignedFloatBuffer AudioBuffer;
FAlignedFloatBuffer ResampledAudioBuffer;
FAlignedFloatBuffer DownmixedResampledAudioBuffer;
FAlignedFloatBuffer DownmixChannelMap;
// Number of channels and sample rate for the external endpoint.
int32 NumChannels;
float SampleRate;
// This is used if the endpoint has a different sample rate than our audio engine.
Audio::FResampler Resampler;
bool bShouldResample;
// for soundfield endpoints, this is the buffer we use to send audio to the endpoint.
TUniquePtr<ISoundfieldAudioPacket> AudioPacket;
FEndpointData()
: NumChannels(0)
, SampleRate(0.0f)
, bShouldResample(false)
{}
void Reset()
{
AudioBuffer.Reset();
ResampledAudioBuffer.Reset();
DownmixedResampledAudioBuffer.Reset();
DownmixChannelMap.Reset();
NonSoundfieldEndpoint.Reset();
SoundfieldEndpoint.Reset();
}
};
FEndpointData EndpointData;
float CurrentOutputVolume;
float TargetOutputVolume;
float CurrentWetLevel;
float TargetWetLevel;
float CurrentDryLevel;
float TargetDryLevel;
FModulationDestination VolumeMod;
FModulationDestination DryLevelMod;
FModulationDestination WetLevelMod;
float VolumeModBaseDb = 0.f;
float DryModBaseDb = MIN_VOLUME_DECIBELS;
float WetModBaseDb = 0.f;
// modifiers set from BP code
float VolumeModifier = 1.f;
float DryLevelModifier = 1.f;
float WetLevelModifier = 1.f;
// Envelope following data
float EnvelopeValues[AUDIO_MIXER_MAX_OUTPUT_CHANNELS];
Audio::FEnvelopeFollower EnvelopeFollower;
int32 EnvelopeNumChannels;
FCriticalSection EnvelopeCriticalSection;
// Spectrum analyzer. Created and destroyed on the audio thread.
FCriticalSection SpectrumAnalyzerCriticalSection;
FSoundSpectrumAnalyzerSettings SpectrumAnalyzerSettings;
TSharedPtr<FAsyncSpectrumAnalyzer, ESPMode::ThreadSafe> SpectrumAnalyzer;
// This buffer is used to downmix the submix output to mono before submitting it to the SpectrumAnalyzer.
FAlignedFloatBuffer MonoMixBuffer;
// The dry channel buffer
FAlignedFloatBuffer DryChannelBuffer;
// Submix command queue to shuffle commands from audio thread to audio render thread.
TQueue<TFunction<void()>> CommandQueue;
// List of submix buffer listeners. (mutable for pruning stale weak references)
mutable TArray<TWeakPtr<ISubmixBufferListener>> BufferListenerPtrs;
// Critical section used for modifying and interacting with buffer listeners
mutable FCriticalSection BufferListenerCriticalSection;
// This buffer is used for recorded output of the submix.
FAlignedFloatBuffer RecordingData;
// Returns the number of submix effects
int32 NumSubmixEffects;
// Bool set to true when this submix is recording data.
uint8 bIsRecording : 1;
// Whether or not this submix is muted.
uint8 bIsBackgroundMuted : 1;
// Whether or not auto-disablement is enabled. If true, the submix will disable itself.
uint8 bAutoDisable : 1;
// Whether or not the submix is currently rendering audio. I.e. audio was sent to it and mixing it, or any of its child submixes are rendering audio.
uint8 bIsSilent : 1;
// Whether or not we're currently disabled (i.e. the submix has been silent)
uint8 bIsCurrentlyDisabled : 1;
// The time to wait to disable the submix if the auto-disablement is active.
double AutoDisableTime;
// The time that the first full silent buffer was detected in the submix. Submix will auto-disable if the timeout is reached and the submix has bAutoDisable set to true.
double SilenceTimeStartSeconds;
// Bool set to true when envelope following is enabled
FThreadSafeBool bIsEnvelopeFollowing;
// Multi-cast delegate to broadcast envelope data from this submix instance
FOnSubmixEnvelope OnSubmixEnvelope;
struct FSpectralAnalysisBandInfo
{
FInlineEnvelopeFollower EnvelopeFollower;
};
struct FSpectrumAnalysisDelegateInfo
{
FSoundSpectrumAnalyzerDelegateSettings DelegateSettings;
FOnSubmixSpectralAnalysis OnSubmixSpectralAnalysis;
TUniquePtr<ISpectrumBandExtractor> SpectrumBandExtractor;
TArray<FSpectralAnalysisBandInfo> SpectralBands;
float LastUpdateTime = -1.0f;
float UpdateDelta = 0.0f;
FSpectrumAnalysisDelegateInfo()
{
}
FSpectrumAnalysisDelegateInfo(FSpectrumAnalysisDelegateInfo&& Other)
{
OnSubmixSpectralAnalysis = Other.OnSubmixSpectralAnalysis;
SpectrumBandExtractor.Reset(Other.SpectrumBandExtractor.Release());
DelegateSettings = Other.DelegateSettings;
SpectralBands = Other.SpectralBands;
}
~FSpectrumAnalysisDelegateInfo()
{
}
};
TArray<FSpectrumAnalysisDelegateInfo> SpectralAnalysisDelegates;
// Bool set to true when spectrum analysis is enabled
FThreadSafeBool bIsSpectrumAnalyzing;
// Critical section used for when we are appending recorded data.
FCriticalSection RecordingCriticalSection;
// Critical section for mutation of the effect chain.
FCriticalSection EffectChainMutationCriticalSection;
// Handle back to the owning USoundSubmix. Used when the device is shutdown to prematurely end a recording.
TWeakObjectPtr<const USoundSubmixBase> OwningSubmixObject;
Audio::FPatchSplitter PatchSplitter;
TUniquePtr<IAudioLink> AudioLinkInstance;
friend class FMixerDevice;
private:
AUDIOMIXER_API void SendAudioToRegisteredAudioBuses(FAlignedFloatBuffer& OutAudioBuffer);
void UnregisterBufferListenerInternal(UPTRINT ListenerBufferPtr);
void PruneSubmixBufferListeners();
// Registered audio buses
TMap<Audio::FAudioBusKey, Audio::FPatchInput> AudioBuses;
FAlignedFloatBuffer AudioBusSendBuffer;
};
}