// 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); };