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

225 lines
5.6 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Containers/Queue.h"
#include "Sound/SoundWaveProcedural.h"
#include "Sound/SoundGenerator.h"
#include "AudioDecompress.h"
#include "AudioMixerBuffer.h"
class USoundWave;
namespace Audio
{
class FMixerBuffer;
class FMixerSourceBuffer;
// Data needed for a procedural audio task
struct FProceduralAudioTaskData
{
// The source which owns this decoding task
FMixerSourceBuffer* SourceBuffer;
// The sound generator to use to generate audio
ISoundGeneratorPtr SoundGenerator;
// The audio buffer to fill from the results of the generation
float* AudioData;
// The size of the audio buffer
int32 NumSamples;
// Force decodes to execute synchronously
bool bForceSyncDecode;
FProceduralAudioTaskData()
: SourceBuffer(nullptr)
, AudioData(nullptr)
, NumSamples(0)
, bForceSyncDecode(false)
{}
};
// Data needed for a decode audio task
struct FDecodeAudioTaskData
{
// A pointer to a buffer of audio which will be decoded to
float* AudioData;
// Decompression state for decoder
ICompressedAudioInfo* DecompressionState;
// The buffer type for the decoder
Audio::EBufferType::Type BufferType;
// Number of channels of the decoder
int32 NumChannels;
// The number of frames which are precached
int32 NumPrecacheFrames;
// The number of frames to decode
int32 NumFramesToDecode;
// Whether or not this sound is intending to be looped
bool bLoopingMode;
// Whether or not to skip the first buffer
bool bSkipFirstBuffer;
// Force this decoding operation to occur synchronously,
// regardless of the value of au.ForceSyncAudioDecodes. (used by time synth)
bool bForceSyncDecode;
FDecodeAudioTaskData()
: AudioData(nullptr)
, DecompressionState(nullptr)
, BufferType(Audio::EBufferType::Invalid)
, NumChannels(0)
, NumPrecacheFrames(0)
, NumFramesToDecode(0)
, bLoopingMode(false)
, bSkipFirstBuffer(false)
, bForceSyncDecode(false)
{}
};
// Data needed for a header parse audio task
struct FHeaderParseAudioTaskData
{
// The mixer buffer object which results will be written to
FMixerBuffer* MixerBuffer;
// The sound wave object which contains the encoded file
USoundWave* SoundWave;
FHeaderParseAudioTaskData()
: MixerBuffer(nullptr)
, SoundWave(nullptr)
{}
};
// Results from procedural audio task
struct FProceduralAudioTaskResults
{
int32 NumSamplesWritten;
bool bIsFinished;
float RelativeRenderCost = 1.f;
#if ENABLE_AUDIO_DEBUG
double CPUDuration = 0.0;
#endif // if ENABLE_AUDIO_DEBUG
FProceduralAudioTaskResults()
: NumSamplesWritten(0)
, bIsFinished(false)
{}
};
// Results from decode audio task
struct FDecodeAudioTaskResults
{
// Whether or not the audio buffer looped
bool bIsFinishedOrLooped;
int32 NumSamplesWritten = 0;
#if ENABLE_AUDIO_DEBUG
double CPUDuration = 0;
#endif // if ENABLE_AUDIO_DEBUG
FDecodeAudioTaskResults()
: bIsFinishedOrLooped(false)
{}
};
// The types of audio tasks
enum class EAudioTaskType
{
// The job is a procedural sound wave job to generate more audio
Procedural,
// The job is a header decode job
Header,
// The job is a decode job
Decode,
// The job is invalid (or unknown)
Invalid,
};
// Handle to an in-flight decode job. Can be queried and used on any thread.
class IAudioTask
{
public:
virtual ~IAudioTask() {}
// Queries if the decode job has finished.
virtual bool IsDone() const = 0;
// Returns the job type of the handle.
virtual EAudioTaskType GetType() const = 0;
// Ensures the completion of the decode operation.
virtual void EnsureCompletion() = 0;
// Cancel the decode operation
virtual void CancelTask() = 0;
// Returns the result of a procedural sound generate job
virtual void GetResult(FProceduralAudioTaskResults& OutResult) {};
// Returns the result of a decode job
virtual void GetResult(FDecodeAudioTaskResults& OutResult) {};
};
// Creates a task to decode a decoded file header
IAudioTask* CreateAudioTask(Audio::FDeviceId InDeviceId, const FHeaderParseAudioTaskData& InJobData);
// Creates a task for a procedural sound wave generation
IAudioTask* CreateAudioTask(Audio::FDeviceId InDeviceId, const FProceduralAudioTaskData& InJobData);
// Creates a task to decode a chunk of audio
IAudioTask* CreateAudioTask(Audio::FDeviceId InDeviceId, const FDecodeAudioTaskData& InJobData);
#if ENABLE_AUDIO_DEBUG
struct FScopeDecodeTimer
{
FScopeDecodeTimer(double* OutResultSeconds)
: Result(OutResultSeconds)
{
StartCycle = FPlatformTime::Cycles64();
}
~FScopeDecodeTimer()
{
uint64 EndCycle = FPlatformTime::Cycles64();
if (Result)
{
*Result = static_cast<double>(EndCycle - StartCycle) * FPlatformTime::GetSecondsPerCycle64();
}
}
double* Result = nullptr;
uint64 StartCycle = 0;
};
#endif // if ENABLE_AUDIO_DEBUG
// Creates a queue for audio decode requests with a specific Id. Tasks
// created with this Id will not be started immediately upon creation,
// but will instead be queued up to await a start "kick" later. NOTE:
// "kicking" the queue is the responsibility of the system that creates
// the queue, typically someplace like in a FOnAudioDevicePostRender delegate!
void CreateSynchronizedAudioTaskQueue(AudioTaskQueueId QueueId);
// Destroys an audio decode task queue. Tasks currently queued up are
// optionally started.
void DestroySynchronizedAudioTaskQueue(AudioTaskQueueId QueueId, bool RunCurrentQueue = false);
// "Kicks" all of the audio decode tasks currentlyt in the specified queue.
int KickQueuedTasks(AudioTaskQueueId QueueId);
}