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

201 lines
6.0 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "AudioMixerBuffer.h"
#include "AudioMixerSourceManager.h"
#include "Sound/SoundWave.h"
#include "Sound/SoundGenerator.h"
namespace Audio
{
static const int32 MAX_BUFFERS_QUEUED = 3;
static const int32 LOOP_FOREVER = -1;
extern int32 DirectProceduralRenderingCVar;
struct FProceduralAudioTaskData;
struct FProceduralAudioTaskResults;
struct FRawPCMDataBuffer
{
uint8* Data;
uint32 DataSize;
int32 LoopCount;
uint32 CurrentSample;
uint32 NumSamples;
bool GetNextBuffer(TArrayView<float> OutSourceBufferPtr, const uint32 NumSampleToGet);
FRawPCMDataBuffer()
: Data(nullptr)
, DataSize(0)
, LoopCount(0)
, CurrentSample(0)
, NumSamples(0)
{}
};
/** Enum describing the data-read mode of an audio buffer. */
enum class EBufferReadMode : uint8
{
/** Read the next buffer asynchronously. */
Asynchronous,
/** Read the next buffer asynchronously but skip the first chunk of audio. */
AsynchronousSkipFirstFrame
};
using FMixerSourceBufferPtr = TSharedPtr<class FMixerSourceBuffer, ESPMode::ThreadSafe>;
struct FMixerSourceBufferInitArgs
{
FDeviceId AudioDeviceID = 0;
uint64 AudioComponentID = 0;
uint64 InstanceID = 0;
int32 SampleRate = 0;
int32 AudioMixerNumOutputFrames = 0;
FMixerBuffer* Buffer = nullptr;
USoundWave* SoundWave = nullptr;
ELoopingMode LoopingMode = ELoopingMode::LOOP_Never;
float StartTime = 0.0f;
bool bIsSeeking = false;
bool bForceSyncDecode = false;
bool bIsPreviewSound = false;
};
/** Class which handles decoding audio for a particular source buffer. */
class FMixerSourceBuffer : public ISoundWaveClient
{
public:
static FMixerSourceBufferPtr Create(FMixerSourceBufferInitArgs& InArgs, TArray<FAudioParameter>&& InDefaultParams=TArray<FAudioParameter>());
~FMixerSourceBuffer();
bool Init();
// Sets the decoder to use for realtime async decoding
void SetDecoder(ICompressedAudioInfo* InCompressedAudioInfo);
// Sets the raw PCM data buffer to use for the source buffer
void SetPCMData(const FRawPCMDataBuffer& InPCMDataBuffer);
// Sets the precached buffers
void SetCachedRealtimeFirstBuffers(TArray<uint8>&& InPrecachedBuffer);
// Called by source manager when needing more buffers
void OnBufferEnd();
// Return the number of buffers enqueued on the mixer source buffer
int32 GetNumBuffersQueued() const;
// Returns the next enqueued buffer, returns nullptr if no buffers enqueued
TSharedPtr<FAlignedFloatBuffer, ESPMode::ThreadSafe> GetNextBuffer();
// Returns if buffer looped
bool DidBufferLoop() const { return bLoopCallback; }
// Returns true if buffer finished
bool DidBufferFinish() const { return bBufferFinished; }
// Returns true if the last buffer has been read
bool IsEndOfAudio() const;
// Called to start an async task to read more data
bool ReadMoreRealtimeData(ICompressedAudioInfo* InDecoder, int32 BufferIndex, EBufferReadMode BufferReadMode);
// Returns true if async task is in progress
bool IsAsyncTaskInProgress() const;
// Returns true if the async task is done
bool IsAsyncTaskDone() const;
// Returns some diagnostic state
struct FDiagnosticState
{
FName WaveName;
float RunTimeInSecs=0.f;
bool bInFlight=false;
bool bProcedural=false;
};
void GetDiagnosticState(FDiagnosticState& OutState);
// Ensures the async task finishes
void EnsureAsyncTaskFinishes();
// Begin and end generation on the audio render thread (audio mixer only)
void OnBeginGenerate();
void OnEndGenerate();
void ClearWave() { SoundWave = nullptr; }
#if ENABLE_AUDIO_DEBUG
double GetCPUCoreUtilization() const;
#endif // ENABLE_AUDIO_DEBUG
// Returns the runtime render cost
float GetRelativeRenderCost() const;
// Note: can be called either directly on the rendering thread or from a background decode task.
void DoProceduralRendering(const FProceduralAudioTaskData& ProceduralTaskData, FProceduralAudioTaskResults& ProceduralResult);
void FinishProceduralRendering(const FProceduralAudioTaskResults& TaskResult);
private:
FMixerSourceBuffer(FMixerSourceBufferInitArgs& InArgs, TArray<FAudioParameter>&& InDefaultParams);
void SubmitInitialPCMBuffers();
void SubmitInitialRealtimeBuffers();
void SubmitRealTimeSourceData(const bool bFinishedOrLooped);
void ProcessRealTimeSource();
void SubmitBuffer(TSharedPtr<FAlignedFloatBuffer, ESPMode::ThreadSafe> InSourceVoiceBuffer);
void DeleteDecoder();
int32 NumBuffersQeueued;
FRawPCMDataBuffer RawPCMDataBuffer;
TArray<TSharedPtr<FAlignedFloatBuffer, ESPMode::ThreadSafe>> SourceVoiceBuffers;
TQueue<TSharedPtr<FAlignedFloatBuffer, ESPMode::ThreadSafe>> BufferQueue;
int32 CurrentBuffer;
// SoundWaves are only set for procedural sound waves
USoundWave* SoundWave;
ISoundGeneratorPtr SoundGenerator;
IAudioTask* AsyncRealtimeAudioTask;
ICompressedAudioInfo* DecompressionState;
ELoopingMode LoopingMode;
int32 NumChannels;
Audio::EBufferType::Type BufferType;
int32 NumPrecacheFrames;
Audio::FDeviceId AuioDeviceID;
uint64 InstanceID;
TArray<uint8> CachedRealtimeFirstBuffer;
FName WaveName;
uint64 AsyncTaskStartTimeInCycles=0;
#if ENABLE_AUDIO_DEBUG
int32 SampleRate = 0;
std::atomic<double> CPUCoreUtilization = 0.0;
void UpdateCPUCoreUtilization(double InCPUTime, double InAudioTime);
#endif // ENABLE_AUDIO_DEBUG
std::atomic<float> RelativeRenderCost = 1.0f;
void SetRelativeRenderCost(float InRelativeRenderCost);
mutable FCriticalSection SoundWaveCritSec;
mutable FCriticalSection DecodeTaskCritSec;
uint32 bInitialized : 1;
uint32 bBufferFinished : 1;
uint32 bPlayedCachedBuffer : 1;
uint32 bIsSeeking : 1;
uint32 bLoopCallback : 1;
uint32 bProcedural : 1;
uint32 bIsBus : 1;
uint32 bForceSyncDecode : 1;
uint32 bHasError : 1;
uint32 bDirectRendering : 1;
virtual bool OnBeginDestroy(class USoundWave* Wave) override;
virtual bool OnIsReadyForFinishDestroy(class USoundWave* Wave) const override;
virtual void OnFinishDestroy(class USoundWave* Wave) override;
};
}