// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
#pragma once
#include "Engine/Content/BinaryAsset.h"
#include "Engine/Streaming/StreamableResource.h"
#include "Engine/Content/WeakAssetReference.h"
#include "Engine/Threading/ThreadPoolTask.h"
#include "Types.h"
#include "Config.h"
class AudioSource;
///
/// Audio clip stores audio data in a compressed or uncompressed format using a binary asset. Clips can be provided to audio sources or other audio methods to be played.
///
///
API_CLASS(NoSpawn) class FLAXENGINE_API AudioClip : public BinaryAsset, public StreamableResource
{
DECLARE_BINARY_ASSET_HEADER(AudioClip, 2);
public:
///
/// Audio Clip resource header structure, version 2. Added on 08.08.2019.
///
struct Header
{
AudioFormat Format;
AudioDataInfo Info;
bool Is3D;
bool Streamable;
uint32 OriginalSize;
uint32 ImportedSize;
uint32 SamplesPerChunk[ASSET_FILE_DATA_CHUNKS]; // Amount of audio samples (for all channels) stored per asset chunk (uncompressed data samples count)
};
///
/// Audio clip streaming task
///
class StreamingTask : public ThreadPoolTask
{
private:
WeakAssetReference _asset;
FlaxStorage::LockData _dataLock;
public:
///
/// Init
///
/// Parent asset
StreamingTask(AudioClip* asset)
: _asset(asset)
, _dataLock(asset->Storage->Lock())
{
}
public:
// [ThreadPoolTask]
bool HasReference(Object* resource) const override
{
return _asset == resource;
}
protected:
// [ThreadPoolTask]
bool Run() override;
void OnEnd() override;
};
private:
int32 _totalChunks;
int32 _totalChunksSize;
StreamingTask* _streamingTask;
float _buffersStartTimes[ASSET_FILE_DATA_CHUNKS + 1];
public:
///
/// Finalizes an instance of the class.
///
~AudioClip();
public:
///
/// The audio clip header data.
///
Header AudioHeader;
///
/// The audio backend buffers (internal ids) collection used by this audio clip.
///
Array> Buffers;
///
/// The streaming cache. Contains indices of chunks to stream. If empty no streaming required. Managed by AudioStreamingHandler and used by the Audio streaming tasks.
///
Array> StreamingQueue;
public:
///
/// Gets the audio data format.
///
API_PROPERTY() FORCE_INLINE AudioFormat Format() const
{
return AudioHeader.Format;
}
///
/// Gets the audio data info metadata.
///
API_PROPERTY() FORCE_INLINE const AudioDataInfo& Info() const
{
return AudioHeader.Info;
}
///
/// Returns true if the sound source is three dimensional (volume and pitch varies based on listener distance and velocity).
///
API_PROPERTY() FORCE_INLINE bool Is3D() const
{
return AudioHeader.Is3D;
}
///
/// Returns true if the sound is using data streaming.
///
API_PROPERTY() FORCE_INLINE bool IsStreamable() const
{
return AudioHeader.Streamable;
}
///
/// Returns true if the sound data is during streaming by an async task.
///
API_PROPERTY() FORCE_INLINE bool IsStreamingTaskActive() const
{
return _streamingTask != nullptr;
}
///
/// Gets the length of the audio clip (in seconds).
///
API_PROPERTY() float GetLength() const
{
return AudioHeader.Info.GetLength();
}
public:
///
/// Gets the buffer start time (in seconds).
///
/// Index of the buffer.
/// The buffer start time.
float GetBufferStartTime(int32 bufferIndex) const;
///
/// Gets the index of the first buffer to bind for the audio source data streaming.
///
/// The time (in seconds).
/// The offset time from the chunk start time (in seconds).
/// The buffer index.
int32 GetFirstBufferIndex(float time, float& offset) const;
public:
///
/// Extracts the source audio data from the asset storage. Loads the whole asset. The result data is in an asset format.
///
/// The result data.
/// The result data format header info.
/// True if failed, otherwise false.
API_FUNCTION() bool ExtractData(API_PARAM(Out) Array& resultData, API_PARAM(Out) AudioDataInfo& resultDataInfo);
///
/// Extracts the raw audio data (PCM format) from the asset storage and converts it to the normalized float format (in range [-1;1]). Loads the whole asset.
///
/// The result data.
/// The result data format header info. That output data has 32 bits float data not the signed PCM data.
/// True if failed, otherwise false.
API_FUNCTION() bool ExtractDataFloat(API_PARAM(Out) Array& resultData, API_PARAM(Out) AudioDataInfo& resultDataInfo);
///
/// Extracts the raw audio data (PCM format) from the asset storage. Loads the whole asset.
///
/// The result data.
/// The result data format header info.
/// True if failed, otherwise false.
API_FUNCTION() bool ExtractDataRaw(API_PARAM(Out) Array& resultData, API_PARAM(Out) AudioDataInfo& resultDataInfo);
public:
// [BinaryAsset]
void CancelStreaming() override;
// [StreamableResource]
int32 GetMaxResidency() const override;
int32 GetCurrentResidency() const override;
int32 GetAllocatedResidency() const override;
bool CanBeUpdated() const override;
Task* UpdateAllocation(int32 residency) override;
Task* CreateStreamingTask(int32 residency) override;
void CancelStreamingTasks() override;
protected:
// [BinaryAsset]
bool init(AssetInitData& initData) override;
LoadResult load() override;
void unload(bool isReloading) override;
private:
// Writes audio samples into Audio Backend buffer and handles automatic decompression or format conversion for runtime playback.
bool WriteBuffer(int32 chunkIndex);
};