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

882 lines
30 KiB
C++

// 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<FSoundWaveProxy, ESPMode::ThreadSafe>;
using FSoundWavePtr = TSharedPtr<FSoundWaveData, ESPMode::ThreadSafe>;
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<EAudioMixerChannel::Type> 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<FAudioPlatformDeviceInfo> FindActiveOutputDevice(FName InDeviceID) const = 0;
virtual TArray<FAudioPlatformDeviceInfo> GetAllActiveOutputDevices() const = 0;
virtual TOptional<FAudioPlatformDeviceInfo> 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<FAudioPlatformDeviceInfo> 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<EAudioOutputStreamState::Type> 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<const uint8> 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<uint8> 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<FAudioPlatformDeviceInfo> 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<uint32> 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<uint8>(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<const uint8>& InOutPoppedAudio);
/** Performs buffer fades for shutdown/startup of audio mixer. */
AUDIOMIXERCORE_API void ApplyPrimaryAttenuation(TArrayView<const uint8>& InOutPoppedAudio);
template<typename BufferType>
void ApplyAttenuationInternal(TArrayView<BufferType>& 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<void()> 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<FRunnableThread> 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<FMixerNullCallback> 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<TUniquePtr<FDeviceSwapResult>>&& 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<TUniquePtr<FDeviceSwapResult>> 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