// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "Async/Future.h" #include "AudioMixerLog.h" #include "AudioMixerNullDevice.h" #include "AudioMixerTypes.h" #include "Containers/Array.h" #include "Containers/ArrayView.h" #include "Containers/Set.h" #include "Containers/UnrealString.h" #include "CoreMinimal.h" #include "DSP/BufferVectorOperations.h" #include "DSP/Dsp.h" #include "DSP/ParamInterpolator.h" #include "HAL/CriticalSection.h" #include "HAL/PlatformMath.h" #include "HAL/Runnable.h" #include "HAL/ThreadSafeBool.h" #include "Logging/LogCategory.h" #include "Logging/LogMacros.h" #include "Logging/LogVerbosity.h" #include "Misc/AssertionMacros.h" #include "Misc/Optional.h" #include "Misc/ScopeLock.h" #include "Misc/SingleThreadRunnable.h" #include "Modules/ModuleInterface.h" #include "Stats/Stats.h" #include "Templates/Function.h" #include "Templates/SharedPointer.h" #include "Templates/UniquePtr.h" #include "Trace/Detail/Channel.h" #include "UObject/NameTypes.h" #define UE_API AUDIOMIXERCORE_API class FEvent; class FRunnableThread; class FThreadSafeCounter; namespace Audio { class FMixerNullCallback; } // defines used for AudioMixer.h #define AUDIO_PLATFORM_LOG_ONCE(INFO, VERBOSITY) (AudioMixerPlatformLogOnce(INFO, FString(__FILE__), __LINE__, ELogVerbosity::VERBOSITY)) #define AUDIO_PLATFORM_ERROR(INFO) (AudioMixerPlatformLogOnce(INFO, FString(__FILE__), __LINE__, ELogVerbosity::Error)) #ifndef AUDIO_MIXER_ENABLE_DEBUG_MODE // This define enables a bunch of more expensive debug checks and logging capabilities that are intended to be off most of the time even in debug builds of game/editor. #if (UE_BUILD_SHIPPING || UE_BUILD_TEST) #define AUDIO_MIXER_ENABLE_DEBUG_MODE 0 #else #define AUDIO_MIXER_ENABLE_DEBUG_MODE 1 #endif #endif // Enable debug checking for audio mixer #if AUDIO_MIXER_ENABLE_DEBUG_MODE #define AUDIO_MIXER_CHECK(expr) ensure(expr) #define AUDIO_MIXER_CHECK_GAME_THREAD(_MixerDevice) (_MixerDevice->CheckAudioThread()) #define AUDIO_MIXER_CHECK_AUDIO_PLAT_THREAD(_MixerDevice) (_MixerDevice->CheckAudioRenderingThread()) #else #define AUDIO_MIXER_CHECK(expr) #define AUDIO_MIXER_CHECK_GAME_THREAD(_MixerDevice) #define AUDIO_MIXER_CHECK_AUDIO_PLAT_THREAD(_MixerDevice) #endif #define AUDIO_MIXER_MAX_OUTPUT_CHANNELS 8 // Max number of speakers/channels supported (7.1) #define AUDIO_MIXER_DEFAULT_DEVICE_INDEX INDEX_NONE // Cycle stats for audio mixer DECLARE_STATS_GROUP(TEXT("AudioMixer"), STATGROUP_AudioMixer, STATCAT_Advanced); // Tracks the time for the full render block DECLARE_CYCLE_STAT_EXTERN(TEXT("Render Audio"), STAT_AudioMixerRenderAudio, STATGROUP_AudioMixer, AUDIOMIXERCORE_API); namespace EAudioMixerChannel { /** Enumeration values represent sound file or speaker channel types. */ enum Type { FrontLeft, FrontRight, FrontCenter, LowFrequency, BackLeft, BackRight, FrontLeftOfCenter, FrontRightOfCenter, BackCenter, SideLeft, SideRight, TopCenter, TopFrontLeft, TopFrontCenter, TopFrontRight, TopBackLeft, TopBackCenter, TopBackRight, Unknown, ChannelTypeCount, DefaultChannel = FrontLeft }; inline constexpr int32 MaxSupportedChannel = EAudioMixerChannel::TopCenter; inline const TCHAR* ToString(EAudioMixerChannel::Type InType) { switch (InType) { case FrontLeft: return TEXT("FrontLeft"); case FrontRight: return TEXT("FrontRight"); case FrontCenter: return TEXT("FrontCenter"); case LowFrequency: return TEXT("LowFrequency"); case BackLeft: return TEXT("BackLeft"); case BackRight: return TEXT("BackRight"); case FrontLeftOfCenter: return TEXT("FrontLeftOfCenter"); case FrontRightOfCenter: return TEXT("FrontRightOfCenter"); case BackCenter: return TEXT("BackCenter"); case SideLeft: return TEXT("SideLeft"); case SideRight: return TEXT("SideRight"); case TopCenter: return TEXT("TopCenter"); case TopFrontLeft: return TEXT("TopFrontLeft"); case TopFrontCenter: return TEXT("TopFrontCenter"); case TopFrontRight: return TEXT("TopFrontRight"); case TopBackLeft: return TEXT("TopBackLeft"); case TopBackCenter: return TEXT("TopBackCenter"); case TopBackRight: return TEXT("TopBackRight"); case Unknown: return TEXT("Unknown"); default: return TEXT("UNSUPPORTED"); } } } class FSoundWaveData; class FSoundWaveProxy; class ICompressedAudioInfo; class USoundWave; using FSoundWaveProxyPtr = TSharedPtr; using FSoundWavePtr = TSharedPtr; namespace Audio { /** Structure to hold platform device information **/ struct FAudioPlatformDeviceInfo { /** The name of the audio device */ FString Name; /** ID of the device. */ FString DeviceId; /** The number of channels supported by the audio device */ int32 NumChannels; /** The number of channels above the base stereo or 7.1 channels supported by the audio device */ int32 NumDirectOutChannels; /** The sample rate of the audio device */ int32 SampleRate; /** The data format of the audio stream */ EAudioMixerStreamDataFormat::Type Format; /** The output channel array of the audio device */ TArray OutputChannelArray; /** Whether or not this device is the system default */ uint8 bIsSystemDefault : 1; FAudioPlatformDeviceInfo() { Reset(); } void Reset() { Name = TEXT("Unknown"); DeviceId = TEXT("Unknown"); NumChannels = 0; NumDirectOutChannels = 0; SampleRate = 0; Format = EAudioMixerStreamDataFormat::Unknown; OutputChannelArray.Reset(); bIsSystemDefault = false; } }; /** Platform independent audio mixer interface. */ class IAudioMixer { public: /** Callback to generate a new audio stream buffer. */ virtual bool OnProcessAudioStream(FAlignedFloatBuffer& OutputBuffer) = 0; /** Called when audio render thread stream is shutting down. Last function called. Allows cleanup on render thread. */ virtual void OnAudioStreamShutdown() = 0; bool IsMainAudioMixer() const { return bIsMainAudioMixer; } /** Called by AudioMixer to see if we should do a multithreaded device swap */ AUDIOMIXERCORE_API static bool ShouldUseThreadedDeviceSwap(); /** Called by AudioMixer to see if it should reycle the threads: */ AUDIOMIXERCORE_API static bool ShouldRecycleThreads(); /** Called by AudioMixer if it should use Cache for DeviceInfo Enumeration */ UE_DEPRECATED(5.6, "ShouldUseDeviceInfoCache functionality moved to IAudioMixerPlatformInterface.") AUDIOMIXERCORE_API static bool ShouldUseDeviceInfoCache(); protected: IAudioMixer() : bIsMainAudioMixer(false) {} bool bIsMainAudioMixer; }; enum class EDeviceEndpointType { Unknown, Render, Capture }; // Interface for Caching Device Info. class IAudioPlatformDeviceInfoCache { public: // Pure Interface. virtual ~IAudioPlatformDeviceInfoCache() = default; virtual TOptional FindActiveOutputDevice(FName InDeviceID) const = 0; virtual TArray GetAllActiveOutputDevices() const = 0; virtual TOptional FindDefaultOutputDevice() const = 0; /** Determines if the given device Id is that of an aggregate device which uses hardware Ids as device Ids. */ virtual bool IsAggregateHardwareDeviceId(const FName InDeviceId) const = 0; /** Returns an array of logical audio devices which, when combined, form an aggregate device. */ virtual TArray GetLogicalAggregateDevices(const FName InHardwareId, const EDeviceEndpointType InEndpointType) const = 0; }; /** Defines parameters needed for opening a new audio stream to device. */ struct FAudioMixerOpenStreamParams { /** The audio device index to open. */ uint32 OutputDeviceIndex; /** The number of desired audio frames in audio callback. */ uint32 NumFrames; /** The number of queued buffers to use for the stream. */ int32 NumBuffers; /** Owning platform independent audio mixer ptr.*/ IAudioMixer* AudioMixer; /** The desired sample rate */ uint32 SampleRate; /* The maximum number of sources we will try to decode or playback at once. */ int32 MaxSources; /** Whether or not to try and restore audio to this stream if the audio device is removed (and the device becomes available again). */ bool bRestoreIfRemoved; /** If true, use the system default audio device. If False, AudioDeviceId must contain a valid device Id. */ bool bUseSystemAudioDevice; /** The device id of an audio output device. May be empty if bUseSystemAudioDevice is true. */ FString AudioDeviceId; FAudioMixerOpenStreamParams() : OutputDeviceIndex(INDEX_NONE) , NumFrames(1024) , NumBuffers(1) , AudioMixer(nullptr) , SampleRate(44100) , MaxSources(0) , bRestoreIfRemoved(false) , bUseSystemAudioDevice(true) {} }; struct FAudioOutputStreamInfo { /** The index of the output device for the audio stream. */ uint32 OutputDeviceIndex; FAudioPlatformDeviceInfo DeviceInfo; /** The state of the output audio stream. */ std::atomic StreamState; /** The callback to use for platform-independent layer. */ IAudioMixer* AudioMixer; /** The number of queued buffers to use. */ uint32 NumBuffers; /** Number of output frames */ int32 NumOutputFrames; FAudioOutputStreamInfo() { Reset(); } ~FAudioOutputStreamInfo() { } void Reset() { OutputDeviceIndex = 0; DeviceInfo.Reset(); StreamState = EAudioOutputStreamState::Closed; AudioMixer = nullptr; NumBuffers = 2; NumOutputFrames = 0; } }; enum class EAudioDeviceRole { Console, Multimedia, Communications, COUNT, }; enum class EAudioDeviceState { Active, Disabled, NotPresent, Unplugged, COUNT, }; /** Struct used to store render time analysis data. */ struct FAudioRenderTimeAnalysis { double AvgRenderTime; double MaxRenderTime; double TotalRenderTime; double RenderTimeSinceLastLog; uint32 StartTime; double MaxSinceTick; uint64 RenderTimeCount; int32 RenderInstanceId; AUDIOMIXERCORE_API FAudioRenderTimeAnalysis(); AUDIOMIXERCORE_API void Start(); AUDIOMIXERCORE_API void End(); }; /** Class which wraps an output float buffer and handles conversion to device stream formats. */ class FOutputBuffer { public: FOutputBuffer() : AudioMixer(nullptr) , DataFormat(EAudioMixerStreamDataFormat::Unknown) {} ~FOutputBuffer() = default; /** Initialize the buffer with the given samples and output format. */ AUDIOMIXERCORE_API void Init(IAudioMixer* InAudioMixer, const int32 InNumSamples, const int32 InNumBuffers, const EAudioMixerStreamDataFormat::Type InDataFormat); /** Gets the next mixed buffer from the audio mixer. Returns false if our buffer is already full. */ AUDIOMIXERCORE_API bool MixNextBuffer(); /** Gets the buffer data ptrs. Returns a TArrayView for the full buffer size requested, but in the case of an underrun, OutBytesPopped will be less that the size of the returned TArrayView. */ AUDIOMIXERCORE_API TArrayView PopBufferData(int32& OutBytesPopped) const; /** Gets the number of frames of the buffer. */ AUDIOMIXERCORE_API int32 GetNumSamples() const; /** Returns the format of the buffer. */ EAudioMixerStreamDataFormat::Type GetFormat() const { return DataFormat; } private: IAudioMixer* AudioMixer; // Circular buffer used to buffer audio between the audio render thread and the platform interface thread. mutable Audio::TCircularAudioBuffer CircularBuffer; // Buffer that we render audio to from the IAudioMixer instance associated with this output buffer. Audio::FAlignedFloatBuffer RenderBuffer; // Buffer read by the platform interface thread. mutable Audio::FAlignedByteBuffer PopBuffer; // For non-float situations, this buffer is used to convert RenderBuffer before pushing it to CircularBuffer. FAlignedByteBuffer FormattedBuffer; EAudioMixerStreamDataFormat::Type DataFormat; static AUDIOMIXERCORE_API size_t GetSizeForDataFormat(EAudioMixerStreamDataFormat::Type InDataFormat); int32 CallCounterMixNextBuffer{ 0 }; }; /** Abstract interface for receiving audio device changed notifications */ class IAudioMixerDeviceChangedListener { public: virtual ~IAudioMixerDeviceChangedListener() = default; struct FFormatChangedData { int32 NumChannels = 0; int32 SampleRate = 0; uint32 ChannelBitmask = 0; }; enum class EDisconnectReason { DeviceRemoval, ServerShutdown, FormatChanged, SessionLogoff, SessionDisconnected, ExclusiveModeOverride }; virtual void RegisterDeviceChangedListener() {} virtual void UnregisterDeviceChangedListener() {} virtual void OnDefaultCaptureDeviceChanged(const EAudioDeviceRole InAudioDeviceRole, const FString& DeviceId) {} virtual void OnDefaultRenderDeviceChanged(const EAudioDeviceRole InAudioDeviceRole, const FString& DeviceId) {} virtual void OnDeviceAdded(const FString& DeviceId, bool bIsRenderDevice) {} virtual void OnDeviceRemoved(const FString& DeviceId, bool bIsRenderDevice) {} virtual void OnDeviceStateChanged(const FString& DeviceId, const EAudioDeviceState InState, bool bIsRenderDevice) {} virtual void OnFormatChanged(const FString& InDeviceId, const FFormatChangedData& InFormat) {} virtual void OnSessionDisconnect(EDisconnectReason InReason) {} virtual FString GetDeviceId() const { return FString(); } }; struct FDeviceSwapContext { FDeviceSwapContext() = delete; FDeviceSwapContext(const FString& InRequestedDeviceID, const FString& InReason) : RequestedDeviceId(InRequestedDeviceID), DeviceSwapReason(InReason) {} virtual ~FDeviceSwapContext() = default; FString RequestedDeviceId; FString DeviceSwapReason; TOptional NewDevice; }; struct FDeviceSwapResult { virtual ~FDeviceSwapResult() = default; virtual bool IsNewDeviceReady() const { return false; } FAudioPlatformDeviceInfo DeviceInfo; FString SwapReason; double SuccessfulDurationMs = 0.0; }; /** Abstract interface for mixer platform. */ class IAudioMixerPlatformInterface : public FRunnable, public FSingleThreadRunnable, public IAudioMixerDeviceChangedListener { public: // Virtual functions /** Virtual destructor. */ AUDIOMIXERCORE_API virtual ~IAudioMixerPlatformInterface(); /** Returns the platform API name. */ virtual FString GetPlatformApi() const = 0; /** Initialize the hardware. */ virtual bool InitializeHardware() = 0; /** Check if audio device changed if applicable. Return true if audio device changed. */ virtual bool CheckAudioDeviceChange() { return false; }; /** Resumes playback on new audio device after device change. */ virtual void ResumePlaybackOnNewDevice() {} /** Teardown the hardware. */ virtual bool TeardownHardware() = 0; /** Is the hardware initialized. */ virtual bool IsInitialized() const = 0; /** Returns the number of output devices. */ virtual bool GetNumOutputDevices(uint32& OutNumOutputDevices) { OutNumOutputDevices = 1; return true; } /** Gets the device information of the given device index. */ virtual bool GetOutputDeviceInfo(const uint32 InDeviceIndex, FAudioPlatformDeviceInfo& OutInfo) = 0; /** * Returns the name of the currently used audio device. */ virtual FString GetCurrentDeviceName() const { return CurrentDeviceName; } /** * Can be used to look up the current index for a given device name. * On most platforms, this index may be invalidated if any devices are added or removed. * Returns INDEX_NONE if no mapping is found */ AUDIOMIXERCORE_API virtual int32 GetIndexForDevice(const FString& InDeviceName); /** Gets the platform specific audio settings. */ virtual FAudioPlatformSettings GetPlatformSettings() const = 0; /** Returns the default device index. */ virtual bool GetDefaultOutputDeviceIndex(uint32& OutDefaultDeviceIndex) const { OutDefaultDeviceIndex = 0; return true; } /** Opens up a new audio stream with the given parameters. */ virtual bool OpenAudioStream(const FAudioMixerOpenStreamParams& Params) = 0; /** Closes the audio stream (if it's open). */ virtual bool CloseAudioStream() = 0; /** Starts the audio stream processing and generating audio. */ virtual bool StartAudioStream() = 0; /** Stops the audio stream (but keeps the audio stream open). */ virtual bool StopAudioStream() = 0; /** Resets the audio stream to use a new audio device with the given device ID (empty string means default). */ UE_DEPRECATED(5.6, "Please use new version MoveAudioStreamToNewAudioDevice which takes no parameters.") virtual bool MoveAudioStreamToNewAudioDevice(const FString& InNewDeviceId) { return MoveAudioStreamToNewAudioDevice(); } virtual bool MoveAudioStreamToNewAudioDevice() { return true; } /** Sends a command to swap which output device is being used */ virtual bool RequestDeviceSwap(const FString& DeviceID, bool bInForce, const TCHAR* InReason = nullptr) { return false; } /** Returns the platform device info of the currently open audio stream. */ virtual FAudioPlatformDeviceInfo GetPlatformDeviceInfo() const = 0; /** Submit the given buffer to the platform's output audio device. */ virtual void SubmitBuffer(const uint8* Buffer) {}; /** Submit a buffer that is to be output directly through a discreet device channel. */ virtual void SubmitDirectOutBuffer(const int32 InDirectOutIndex, const Audio::FAlignedFloatBuffer& InBuffer) {}; /** Allows platforms to filter the requested number of frames to render. Some platforms only support specific frame counts. */ virtual int32 GetNumFrames(const int32 InNumReqestedFrames) { return InNumReqestedFrames; } /** Whether or not the platform disables caching of decompressed PCM data (i.e. to save memory on fixed memory platforms) */ virtual bool DisablePCMAudioCaching() const { return false; } /** Whether or not this platform has hardware decompression. */ virtual bool SupportsHardwareDecompression() const { return false; } /** Whether this is an interface for a non-realtime renderer. If true, synch events will behave differently to avoid deadlocks. */ virtual bool IsNonRealtime() const { return false; } /** Return any optional device name defined in platform configuratio. */ virtual FString GetDefaultDeviceName() = 0; // Helper function to gets the channel map type at the given index. AUDIOMIXERCORE_API static bool GetChannelTypeAtIndex(const int32 Index, EAudioMixerChannel::Type& OutType); // Function to stop all audio from rendering. Used on mobile platforms which can suspend the application. virtual void SuspendContext() {} // Function to resume audio rendering. Used on mobile platforms which can suspend the application. virtual void ResumeContext() {} // Function called at the beginning of every call of UpdateHardware on the audio thread. virtual void OnHardwareUpdate() {} // Get the DeviceInfo Cache if one exists. virtual IAudioPlatformDeviceInfoCache* GetDeviceInfoCache() const { return nullptr; } // Determine if the given device info struct is valid for use with this platform virtual bool IsDeviceInfoValid(const FAudioPlatformDeviceInfo& InDeviceInfo) const { return false; } // Subclasses can override to determine if it should use Cache for DeviceInfo Enumeration virtual bool ShouldUseDeviceInfoCache() const { return false; } public: // Public Functions //~ Begin FRunnable AUDIOMIXERCORE_API uint32 Run() override; //~ End FRunnable /** * FSingleThreadRunnable accessor for ticking this FRunnable when multi-threading is disabled. * @return FSingleThreadRunnable Interface for this FRunnable object. */ virtual class FSingleThreadRunnable* GetSingleThreadInterface() override { return this; } //~ Begin FSingleThreadRunnable Interface AUDIOMIXERCORE_API virtual void Tick() override; //~ End FSingleThreadRunnable Interface /** Constructor. */ AUDIOMIXERCORE_API IAudioMixerPlatformInterface(); /** Retrieves the next generated buffer and feeds it to the platform mixer output stream. */ AUDIOMIXERCORE_API void ReadNextBuffer(); /** Reset the fade state (use if reusing audio platform interface, e.g. in main audio device. */ AUDIOMIXERCORE_API virtual void FadeIn(); /** Start a fadeout. Prevents pops during shutdown. */ AUDIOMIXERCORE_API virtual void FadeOut(); /** Returns the last error generated. */ FString GetLastError() const { return LastError; } /** This is called after InitializeHardware() is called. */ AUDIOMIXERCORE_API void PostInitializeHardware(); /** Used to determine if this object is listening to system device change events. */ bool GetIsListeningForDeviceEvents() const { return bIsListeningForDeviceEvents; } void SetIsListeningForDeviceEvents(bool bInListeningForDeviceEvents) { bIsListeningForDeviceEvents = bInListeningForDeviceEvents; } protected: // Run the "main" audio device AUDIOMIXERCORE_API uint32 MainAudioDeviceRun(); // Wrapper around the thread Run. This is virtualized so a platform can fundamentally override the render function. AUDIOMIXERCORE_API virtual uint32 RunInternal(); /** Is called when an error, warning or log is generated. */ inline void AudioMixerPlatformLogOnce(const FString& LogDetails, const FString& FileName, int32 LineNumber, ELogVerbosity::Type InVerbosity = ELogVerbosity::Error) { #if !NO_LOGGING // Log once to avoid Spam. static FCriticalSection Cs; static TSet LogHistory; FScopeLock Lock(&Cs); FString Message = FString::Printf(TEXT("Audio Platform Device: %s (File %s, Line %d)"), *LogDetails, *FileName, LineNumber); if ((ELogVerbosity::Error == InVerbosity) || (ELogVerbosity::Fatal == InVerbosity)) { // Save last error if it was at the error level. LastError = Message; } uint32 Hash = GetTypeHash(Message); if (!LogHistory.Contains(Hash)) { switch (InVerbosity) { case ELogVerbosity::Fatal: UE_LOG(LogAudioMixer, Fatal, TEXT("%s"), *Message); break; case ELogVerbosity::Error: UE_LOG(LogAudioMixer, Error, TEXT("%s"), *Message); break; case ELogVerbosity::Warning: UE_LOG(LogAudioMixer, Warning, TEXT("%s"), *Message); break; case ELogVerbosity::Display: UE_LOG(LogAudioMixer, Display, TEXT("%s"), *Message); break; case ELogVerbosity::Log: UE_LOG(LogAudioMixer, Log, TEXT("%s"), *Message); break; case ELogVerbosity::Verbose: UE_LOG(LogAudioMixer, Verbose, TEXT("%s"), *Message); break; case ELogVerbosity::VeryVerbose: UE_LOG(LogAudioMixer, VeryVerbose, TEXT("%s"), *Message); break; default: UE_LOG(LogAudioMixer, Error, TEXT("%s"), *Message); { static_assert(static_cast(ELogVerbosity::NumVerbosity) == 8, "Missing ELogVerbosity case coverage"); } break; } LogHistory.Add(Hash); } #endif } /** Start generating audio from our mixer. */ AUDIOMIXERCORE_API void BeginGeneratingAudio(); /** Stops the render thread from generating audio. */ AUDIOMIXERCORE_API void StopGeneratingAudio(); // Deprecated - use ApplyPrimaryAttenuation UE_DEPRECATED(5.1, "ApplyMasterAttenuation is deprecated, please use ApplyPrimaryAttenuation instead.") AUDIOMIXERCORE_API void ApplyMasterAttenuation(TArrayView& InOutPoppedAudio); /** Performs buffer fades for shutdown/startup of audio mixer. */ AUDIOMIXERCORE_API void ApplyPrimaryAttenuation(TArrayView& InOutPoppedAudio); template void ApplyAttenuationInternal(TArrayView& InOutBuffer); /** When called, spins up a thread to start consuming output when no audio device is available. */ AUDIOMIXERCORE_API void StartRunningNullDevice(); /** When called, terminates the null device. */ AUDIOMIXERCORE_API void StopRunningNullDevice(); /** Called by platform specific logic to pre-create or create the null renderer thread */ AUDIOMIXERCORE_API void CreateNullDeviceThread(const TFunction InCallback, float InBufferDuration, bool bShouldPauseOnStart); protected: /** The audio device stream info. */ FAudioOutputStreamInfo AudioStreamInfo; FAudioMixerOpenStreamParams OpenStreamParams; /** List of generated output buffers. */ Audio::FOutputBuffer OutputBuffer; /** Whether or not we warned of buffer underrun. */ bool bWarnedBufferUnderrun; /** The audio render thread. */ //FRunnableThread* AudioRenderThread; TUniquePtr AudioRenderThread; /** The render thread sync event. */ FEvent* AudioRenderEvent; /** Critical Section used for times when we need the render loop to halt for the device swap. */ FCriticalSection DeviceSwapCriticalSection; /** This is used if we are attempting to TryLock on DeviceSwapCriticalSection, but a buffer callback is being called in the current thread. */ UE_DEPRECATED(5.6, "bIsInDeviceSwap has been deprecated. AudioStreamInfo.StreamState can provide similar functionality.") FThreadSafeBool bIsInDeviceSwap; /** Event allows you to block until fadeout is complete. */ FEvent* AudioFadeEvent; /** The number of mixer buffers to queue on the output source voice. */ int32 NumOutputBuffers; /** The fade value. Used for fading in/out primary audio. */ float FadeVolume; /** Source param used to fade in and out audio device. */ FParam FadeParam; /** This device name can be used to override the default device being used on platforms that use strings to identify audio devices. */ FString CurrentDeviceName; /** String containing the last generated error. */ FString LastError; int32 CallCounterApplyAttenuationInternal{ 0 }; int32 CallCounterReadNextBuffer{ 0 }; FThreadSafeBool bPerformingFade; FThreadSafeBool bFadedOut; FThreadSafeBool bIsDeviceInitialized; UE_DEPRECATED(5.6, "bMoveAudioStreamToNewAudioDevice has been deprecated. AudioStreamInfo.StreamState can provide similar functionality.") FThreadSafeBool bMoveAudioStreamToNewAudioDevice; FThreadSafeBool bIsUsingNullDevice; FThreadSafeBool bIsGeneratingAudio; /** A Counter to provide the next unique id. */ AUDIOMIXERCORE_API static FThreadSafeCounter NextInstanceID; /** A Unique ID Identifying this instance. Mostly used for logging. */ const int32 InstanceID{ -1 }; private: TUniquePtr NullDeviceCallback; /** Used to determine if this object is listening to system device change events. */ bool bIsListeningForDeviceEvents = true; }; /** Audio mixer platform objects can subclass this in order to add device swap capabilities. */ class FAudioMixerPlatformSwappable : public IAudioMixerPlatformInterface { public: UE_API FAudioMixerPlatformSwappable(); virtual ~FAudioMixerPlatformSwappable() override = default; //~ Begin IAudioMixerPlatformInterface UE_API virtual bool RequestDeviceSwap(const FString& DeviceID, const bool bInForce, const TCHAR* InReason) override; UE_API virtual bool CheckAudioDeviceChange() override; UE_API virtual bool MoveAudioStreamToNewAudioDevice() override; UE_API virtual void ResumePlaybackOnNewDevice() override; //~ End IAudioMixerPlatformInterface /** Called to determine if the current device swap request should be allowed to proceed. */ UE_API virtual bool AllowDeviceSwap(const bool bInForceSwap); /** Initializes a new device swap context with the given parameters */ virtual bool InitializeDeviceSwapContext(const FString& InRequestedDeviceID, const TCHAR* InReason) = 0; /** Called repeatedly to update an active, async device swap */ UE_API virtual bool CheckThreadedDeviceSwap(); /** Called at the beginning of a device swap to perform any needed initialization */ virtual bool PreDeviceSwap() { return true; } /** Kicks of an async device swap task */ virtual void EnqueueAsyncDeviceSwap() = 0; /** Performs a device swap synchronously in the current thread */ virtual void SynchronousDeviceSwap() = 0; /** Called after a device swap completes, providing an opportunity for any needed cleanup */ virtual bool PostDeviceSwap() { return true; } protected: /** Used in OnDeviceAdded for determining if an added device is the same as the original device. */ FString GetOriginalAudioDeviceId() const { return OriginalAudioDeviceId; } void SetOriginalAudioDeviceId(const FString& InAudioDeviceId) { OriginalAudioDeviceId = InAudioDeviceId; } /** Future which holds result of device swap upon completion. */ void SetActiveDeviceSwapFuture(TFuture>&& InFuture) { ActiveDeviceSwap = MoveTemp(InFuture); } void ResetActiveDeviceSwapFuture() { ActiveDeviceSwap.Reset(); } /** The results produced from a device swap operation. The containing future retains memory ownership. * Will be valid until PostDeviceSwap returns. */ const FDeviceSwapResult* GetDeviceSwapResult() const { return ActiveDeviceSwap.IsValid() ? ActiveDeviceSwap.Get().Get() : nullptr; } FDeviceSwapResult* GetDeviceSwapResult() { return ActiveDeviceSwap.IsValid() ? ActiveDeviceSwap.Get().Get() : nullptr; } private: /** Used in OnDeviceAdded for determining if an added device is the same as the original device. */ FString OriginalAudioDeviceId; /** Future which holds result of device swap upon completion. */ TFuture> ActiveDeviceSwap; /** Used to rate limit device swap attempts to ignore multiple requests */ double LastDeviceSwapTime = 0.0; }; } /** * Interface for audio device modules */ class FAudioDevice; /** Defines the interface of a module implementing an audio device and associated classes. */ class IAudioDeviceModule : public IModuleInterface { public: /** Creates a new instance of the audio device implemented by the module. */ virtual bool IsAudioMixerModule() const { return false; } /** Does this class of device support multiclient access to the driver */ virtual bool IsAudioDeviceClassMulticlient() const { return true; } virtual FAudioDevice* CreateAudioDevice() { return nullptr; } virtual Audio::IAudioMixerPlatformInterface* CreateAudioMixerPlatformInterface() { return nullptr; } }; #undef UE_API