Files
UnrealEngine/Engine/Source/Runtime/AdpcmAudioDecoder/Module/Public/Decoders/ADPCMAudioInfo.h
2025-05-18 13:04:45 +08:00

189 lines
7.4 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
/*=============================================================================
ADPCMAudioInfo.h: Unreal audio ADPCM decompression interface object.
=============================================================================*/
#pragma once
#include "CoreMinimal.h"
#include "Audio.h"
#include "AudioDecompress.h"
#include "Sound/SoundWave.h"
#include "ContentStreaming.h"
#define NUM_ADAPTATION_TABLE 16
#define NUM_ADAPTATION_COEFF 7
#define WAVE_FORMAT_LPCM 1
#ifndef WAVE_FORMAT_ADPCM
#define WAVE_FORMAT_ADPCM 2
#endif
#if PLATFORM_SUPPORTS_PRAGMA_PACK
#pragma pack(push, 2)
#endif
struct WaveFormatHeader
{
uint16 wFormatTag; // Format type: 1 = PCM, 2 = ADPCM
uint16 nChannels; // Number of channels (i.e. mono, stereo...).
uint32 nSamplesPerSec; // Sample rate. 44100 or 22050 or 11025 Hz.
uint32 nAvgBytesPerSec; // For buffer estimation = sample rate * BlockAlign.
uint16 nBlockAlign; // Block size of data = Channels times BYTES per sample.
uint16 wBitsPerSample; // Number of bits per sample of mono data.
uint16 cbSize; // The count in bytes of the size of extra information (after cbSize).
};
#if PLATFORM_SUPPORTS_PRAGMA_PACK
#pragma pack(pop)
#endif
namespace ADPCM
{
template <typename T>
static void GetAdaptationCoefficients(T(& OutAdaptationCoefficient1)[NUM_ADAPTATION_COEFF], T(& OutAdaptationCoefficient2)[NUM_ADAPTATION_COEFF])
{
// Magic values as specified by standard
static T AdaptationCoefficient1[] =
{
256, 512, 0, 192, 240, 460, 392
};
static T AdaptationCoefficient2[] =
{
0, -256, 0, 64, 0, -208, -232
};
FMemory::Memcpy(&OutAdaptationCoefficient1, AdaptationCoefficient1, sizeof(AdaptationCoefficient1));
FMemory::Memcpy(&OutAdaptationCoefficient2, AdaptationCoefficient2, sizeof(AdaptationCoefficient2));
}
#if PLATFORM_SUPPORTS_PRAGMA_PACK
#pragma pack(push, 2)
#endif
struct ADPCMFormatHeader
{
WaveFormatHeader BaseFormat;
uint16 wSamplesPerBlock;
uint16 wNumCoef;
int16 aCoef[2 * NUM_ADAPTATION_COEFF];
uint32 SamplesPerChannel; // This is the exact samples per channel for sample precise looping
ADPCMFormatHeader()
{
FMemory::Memzero(this, sizeof(ADPCMFormatHeader));
int16 AdaptationCoefficient1[NUM_ADAPTATION_COEFF];
int16 AdaptationCoefficient2[NUM_ADAPTATION_COEFF];
GetAdaptationCoefficients(AdaptationCoefficient1, AdaptationCoefficient2);
// Interlace the coefficients as pairs
for (int32 Coeff = 0, WriteIndex = 0; Coeff < NUM_ADAPTATION_COEFF; Coeff++)
{
aCoef[WriteIndex++] = AdaptationCoefficient1[Coeff];
aCoef[WriteIndex++] = AdaptationCoefficient2[Coeff];
}
}
};
#if PLATFORM_SUPPORTS_PRAGMA_PACK
#pragma pack(pop)
#endif
};
class FADPCMAudioInfo : public ICompressedAudioInfo
{
public:
ADPCMAUDIODECODER_API FADPCMAudioInfo(void);
ADPCMAUDIODECODER_API virtual ~FADPCMAudioInfo(void);
// ICompressedAudioInfo Interface
ADPCMAUDIODECODER_API virtual bool ReadCompressedInfo(const uint8* InSrcBufferData, uint32 InSrcBufferDataSize, struct FSoundQualityInfo* QualityInfo);
ADPCMAUDIODECODER_API virtual bool ReadCompressedData(uint8* Destination, bool bLooping, uint32 BufferSize);
ADPCMAUDIODECODER_API virtual void SeekToTime(const float SeekTime);
ADPCMAUDIODECODER_API virtual void SeekToFrame(const uint32 SeekFrame);
ADPCMAUDIODECODER_API virtual void ExpandFile(uint8* DstBuffer, struct FSoundQualityInfo* QualityInfo);
virtual void EnableHalfRate(bool HalfRate) {};
virtual uint32 GetSourceBufferSize() const { return SrcBufferDataSize; }
virtual bool UsesVorbisChannelOrdering() const { return false; }
virtual int GetStreamBufferSize() const;
// Additional overrides for streaming
virtual bool SupportsStreaming() const override {return true;}
virtual bool StreamCompressedInfoInternal(const FSoundWaveProxyPtr& InWaveProxy, struct FSoundQualityInfo* QualityInfo) override;
virtual bool StreamCompressedData(uint8* Destination, bool bLooping, uint32 BufferSize, int32& OutNumBytesStreamed) override;
virtual int32 GetCurrentChunkIndex() const override
{
return CurrentChunkIndex;
}
virtual int32 GetCurrentChunkOffset() const override {return CurrentChunkBufferOffset;}
virtual bool ReleaseStreamChunk(bool bBlockUntilReleased) override;
// End of ICompressedAudioInfo Interface
uint16 GetFormatTag()
{
if (WaveInfo.pFormatTag)
{
return *WaveInfo.pFormatTag;
}
else
{
return 0;
}
}
int32 GetNumChannels()
{
return NumChannels;
}
private:
// Wrapper function that returns a pointer to the currently used compressed data.
// If a non-zero chunk is requested, this function also aquires a reference to that chunk
// until we move on to a different chunk.
const uint8* GetLoadedChunk(const FSoundWaveProxyPtr& InSoundWave, uint32 ChunkIndex, uint32& OutChunkSize);
int32 NumConsecutiveReadFailiures;
FWaveModInfo WaveInfo;
const uint8* SrcBufferData;
uint32 SrcBufferDataSize;
uint32 UncompressedBlockSize;
uint32 CompressedBlockSize;
uint32 BlockSize;
int32 StreamBufferSize;
uint32 TotalDecodedSize;
int32 NumChannels;
int32 Format;
uint32 PreviouslyRequestedChunkIndex;
uint8* UncompressedBlockData; // This holds the current block of compressed data for all channels
uint32 CurrentUncompressedBlockSampleIndex; // This is the sample index within the current uncompressed block data
uint32 CurrentChunkIndex; // This is the index that is currently being used, needed by streaming engine to make sure it stays loaded and the next chunk gets preloaded
uint32 CurrentChunkBufferOffset; // This is this byte offset within the current chunk, used by streaming engine to prioritize a load if more then half way through current chunk
uint32 CurrentChunkDataSize; // The size of the current chunk, the first chunk is bigger to accommodate the header info
uint32 TotalSamplesStreamed; // The number of samples streamed so far (per channel)
uint32 TotalSamplesPerChannel; // Number of samples per channel, used to detect when an audio waveform has ended
uint32 SamplesPerBlock; // The number of samples per block
uint32 FirstChunkSampleDataOffset; // The size of the header in the first chunk, used to skip over it when looping or starting the sample over
uint32 FirstChunkSampleDataIndex;
const uint8* CurCompressedChunkData; // A pointer to the current chunk of data
FAudioChunkHandle CurCompressedChunkHandle; // Shared reference to the current chunk of data.
FCriticalSection CurCompressedChunkHandleCriticalSection;
bool bDecompressorReleased; // When the chunk is released, we raise this to true, then early exit on future decodes.
uint32 CurrentCompressedBlockIndex; // For non disk streaming - the current compressed block in the compressed source data
uint32 TotalCompressedBlocksPerChannel; // For non disk streaming - the total number of compressed blocks per channel
uint8 bNewSeekRequest : 1; // Whether or not there is a new seek request to be processed
uint8 bSeekPendingRead : 1; // Whether or not a requested seek is pending a read
uint8 bSeekedFowardToNextChunk : 1; // If this is true, we have already seeked forward to the next chunk while waiting for the current chunk of audio to load.
private:
void ProcessSeekRequest();
void SeekToFrameInternal(const uint32 InSeekFrame);
void ResetSeekState();
std::atomic<uint32> TargetSeekFrame;
FCriticalSection StreamSeekCriticalSection;
};