Encoding changes, UNIX/Mac optimising

This commit is contained in:
Georgy Treshchev
2021-02-19 10:08:21 +01:00
parent 23fd2c3238
commit c0943e2852
2 changed files with 270 additions and 191 deletions
@@ -5,7 +5,6 @@
URuntimeAudioImporterLibrary::URuntimeAudioImporterLibrary(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
}
URuntimeAudioImporterLibrary* URuntimeAudioImporterLibrary::CreateRuntimeAudioImporter()
@@ -15,18 +14,20 @@ URuntimeAudioImporterLibrary* URuntimeAudioImporterLibrary::CreateRuntimeAudioIm
return Importer;
}
URuntimeAudioImporterLibrary* URuntimeAudioImporterLibrary::ImportAudioFromFile(const FString& filePath, TEnumAsByte < AudioFormat > Format, bool DefineFormatAutomatically)
URuntimeAudioImporterLibrary* URuntimeAudioImporterLibrary::ImportAudioFromFile(
const FString& FilePath, TEnumAsByte<AudioFormat> Format, bool DefineFormatAutomatically)
{
AsyncTask(ENamedThreads::AnyThread, [=]() {
GetUSoundWaveFromAudioFile_Internal(filePath, Format, DefineFormatAutomatically);
});
AsyncTask(ENamedThreads::AnyThread, [=]()
{
GetUSoundWaveFromAudioFile_Internal(FilePath, Format, DefineFormatAutomatically);
});
return this;
}
bool URuntimeAudioImporterLibrary::DestroySoundWave(USoundWave* ReadySoundWave) {
if (ReadySoundWave->IsValidLowLevel()) {
bool URuntimeAudioImporterLibrary::DestroySoundWave(USoundWave* ReadySoundWave)
{
if (ReadySoundWave->IsValidLowLevel())
{
//Begin USoundWave destroy (block changing some data)
ReadySoundWave->ConditionalBeginDestroy();
ReadySoundWave->BeginDestroy();
@@ -34,9 +35,10 @@ bool URuntimeAudioImporterLibrary::DestroySoundWave(USoundWave* ReadySoundWave)
//Delete all audio data from physical memory
ReadySoundWave->RawData.RemoveBulkData();
ReadySoundWave->RemoveAudioResource();
AsyncTask(ENamedThreads::AudioThread, [=]() {
AsyncTask(ENamedThreads::AudioThread, [=]()
{
ReadySoundWave->InvalidateCompressedData(true);
});
});
//Finish USoundWave destroy (complete object deletion as UObject)
ReadySoundWave->IsReadyForFinishDestroy(); //This is not needed for verification, but for the initial removal
@@ -44,7 +46,8 @@ bool URuntimeAudioImporterLibrary::DestroySoundWave(USoundWave* ReadySoundWave)
ReadySoundWave->FinishDestroy();
return true;
}
else {
else
{
return false;
}
}
@@ -56,17 +59,19 @@ bool URuntimeAudioImporterLibrary::DestroySoundWave(USoundWave* ReadySoundWave)
#include "ThirdParty/dr_mp3.h"
#include "ThirdParty/dr_flac.h"
/**
* Replacing standard CPP memory functions (malloc, realloc, free) with memory management functions that are maintained by Epic as the engine evolves.
*/
/**
* Replacing standard CPP memory functions (malloc, realloc, free) with memory management functions that are maintained by Epic as the engine evolves.
*/
void* unreal_malloc(size_t sz, void* pUserData)
{
return FMemory::Malloc(sz);
}
void* unreal_realloc(void* p, size_t sz, void* pUserData)
{
return FMemory::Realloc(p, sz);
}
void unreal_free(void* p, void* pUserData)
{
FMemory::Free(p);
@@ -77,9 +82,6 @@ bool URuntimeAudioImporterLibrary::ExportSoundWaveToFile(USoundWave* SoundWaveTo
uint8* PCMData = SoundWaveToExport->RawPCMData;
size_t PCMDataSize = SoundWaveToExport->RawPCMDataSize;
uint32 SampleRate;
uint16 ChannelCount;
drwav_allocation_callbacks allocationCallbacksDecoding;
allocationCallbacksDecoding.pUserData = NULL;
allocationCallbacksDecoding.onMalloc = unreal_malloc;
@@ -88,8 +90,9 @@ bool URuntimeAudioImporterLibrary::ExportSoundWaveToFile(USoundWave* SoundWaveTo
drwav_uint64 framesToWrite = PCMDataSize / sizeof(DR_WAVE_FORMAT_PCM) / SoundWaveToExport->NumChannels;
ChannelCount = SoundWaveToExport->NumChannels;
SampleRate = FGenericPlatformMath::RoundToInt((framesToWrite / (float)SoundWaveToExport->GetDuration() * ChannelCount));
uint16 ChannelCount = SoundWaveToExport->NumChannels;
uint32 SampleRate = FGenericPlatformMath::RoundToInt(
(framesToWrite / (float)SoundWaveToExport->GetDuration() * ChannelCount));
drwav wavEncode;
@@ -100,7 +103,14 @@ bool URuntimeAudioImporterLibrary::ExportSoundWaveToFile(USoundWave* SoundWaveTo
format.sampleRate = SampleRate;
format.bitsPerSample = 16;
if (!drwav_init_file_write_w(&wavEncode, (wchar_t*)PathToExport.GetCharArray().GetData(), &format, &allocationCallbacksDecoding)) {
#if PLATFORM_WINDOWS
if (!drwav_init_file_write_w(&wavEncode, (wchar_t*)PathToExport.GetCharArray().GetData(), &format,
&allocationCallbacksDecoding))
#else
if (!drwav_init_file_write(&wavEncode, TCHAR_TO_UTF8(*PathToExport), &format,
&allocationCallbacksDecoding))
#endif
{
return false;
}
@@ -110,48 +120,61 @@ bool URuntimeAudioImporterLibrary::ExportSoundWaveToFile(USoundWave* SoundWaveTo
return true;
}
URuntimeAudioImporterLibrary* URuntimeAudioImporterLibrary::GetUSoundWaveFromAudioFile_Internal(const FString& filePath, TEnumAsByte < AudioFormat > Format, bool DefineFormatAutomatically)
URuntimeAudioImporterLibrary* URuntimeAudioImporterLibrary::GetUSoundWaveFromAudioFile_Internal(
const FString& FilePath, TEnumAsByte<AudioFormat> Format, bool DefineFormatAutomatically)
{
TEnumAsByte < TranscodingStatus > Status;
if (FPaths::FileExists(filePath) != true) {
TEnumAsByte<TranscodingStatus> Status;
if (FPaths::FileExists(FilePath) != true)
{
Status = TranscodingStatus::AudioDoesNotExist;
// Callback Dispatcher OnResult
OnResult_Internal(nullptr, Status);
}
else {
else
{
int16_t* pSampleData;
uint64 framesToWrite;
uint32 channels;
uint32 sampleRate;
if (DefineFormatAutomatically) {
Format = GetAudioFormat(filePath);
if (DefineFormatAutomatically)
{
Format = GetAudioFormat(FilePath);
}
if (Format == AudioFormat::INVALID) {
if (Format == AudioFormat::INVALID)
{
Status = TranscodingStatus::InvalidAudioFormat;
// Callback Dispatcher OnResult
OnResult_Internal(nullptr, Status);
}
else {
if (TranscodeAudioFileToPCMData((wchar_t*)filePath.GetCharArray().GetData(), Format, Status, framesToWrite, pSampleData, channels, sampleRate) == false) {
else
{
if (TranscodeAudioFileToPCMData(FilePath, Format, Status, framesToWrite, pSampleData, channels, sampleRate)
== false)
{
// Callback Dispatcher OnResult
OnResult_Internal(nullptr, Status);
}
else {
else
{
void* WaveData;
size_t WaveDataSize;
if (TranscodePCMToWAVData(*&WaveData, *&WaveDataSize, framesToWrite, *&pSampleData, channels, sampleRate) == false) {
if (TranscodePCMToWAVData(*&WaveData, *&WaveDataSize, framesToWrite, *&pSampleData, channels,
sampleRate) == false)
{
FMemory::Free(pSampleData);
// Callback Dispatcher OnResult
OnResult_Internal(nullptr, Status);
}
else {
USoundWave* ReadyUSoundWave = GetSoundWaveObject((const uint8*)WaveData, WaveDataSize, Status); // A ready USoundWave static object
else
{
USoundWave* ReadyUSoundWave = GetSoundWaveObject((const uint8*)WaveData, WaveDataSize, Status);
// A ready USoundWave static object
/* Free temporary transcoding data */
FMemory::Free(WaveData);
@@ -164,18 +187,18 @@ URuntimeAudioImporterLibrary* URuntimeAudioImporterLibrary::GetUSoundWaveFromAud
// Callback Dispatcher OnResult
OnResult_Internal(ReadyUSoundWave, Status);
}
}
}
}
return this;
}
class USoundWave* URuntimeAudioImporterLibrary::GetSoundWaveObject(const uint8* WaveData, int32 WaveDataSize, TEnumAsByte < TranscodingStatus >& Status)
class USoundWave* URuntimeAudioImporterLibrary::GetSoundWaveObject(const uint8* WaveData, int32 WaveDataSize,
TEnumAsByte<TranscodingStatus>& Status)
{
USoundWave* sw = NewObject<USoundWave>(USoundWave::StaticClass());
if (!sw) {
if (!sw)
{
Status = TranscodingStatus::USoundWaveDeclarationError;
return nullptr;
}
@@ -190,7 +213,8 @@ class USoundWave* URuntimeAudioImporterLibrary::GetSoundWaveObject(const uint8*
// Callback Dispatcher OnProgress (not completely accurate implementation)
OnProgress_Internal(75);
if (*WaveInfo.pBitsPerSample != 16) {
if (*WaveInfo.pBitsPerSample != 16)
{
Status = TranscodingStatus::UnsupportedBitDepth;
return nullptr;
}
@@ -222,17 +246,25 @@ class USoundWave* URuntimeAudioImporterLibrary::GetSoundWaveObject(const uint8*
// Callback Dispatcher OnProgress (not completely accurate implementation)
OnProgress_Internal(85);
}
else {
if (ErrorReason.Equals(TEXT("Invalid WAVE file."), ESearchCase::CaseSensitive)) {
else
{
if (ErrorReason.Equals(TEXT("Invalid WAVE file."), ESearchCase::CaseSensitive))
{
Status = TranscodingStatus::InvalidWAVEData;
}
else if (ErrorReason.Equals(TEXT("Unsupported WAVE file format: actual bit rate does not match the container size."), ESearchCase::CaseSensitive)) {
else if (ErrorReason.Equals(
TEXT("Unsupported WAVE file format: actual bit rate does not match the container size."),
ESearchCase::CaseSensitive))
{
Status = TranscodingStatus::InvalidBitrate;
}
else if (ErrorReason.Equals(TEXT("Unsupported WAVE file format: subformat identifier not recognized."), ESearchCase::CaseSensitive)) {
else if (ErrorReason.Equals(
TEXT("Unsupported WAVE file format: subformat identifier not recognized."), ESearchCase::CaseSensitive))
{
Status = TranscodingStatus::InvalidSubformat;
}
else {
else
{
Status = TranscodingStatus::UnrecognizedReadWaveError;
}
return nullptr;
@@ -248,7 +280,8 @@ class USoundWave* URuntimeAudioImporterLibrary::GetSoundWaveObject(const uint8*
// Callback Dispatcher OnProgress (not completely accurate implementation)
OnProgress_Internal(95);
if (!sw) {
if (!sw)
{
Status = TranscodingStatus::InvalidUSoundWave;
return nullptr;
}
@@ -256,123 +289,151 @@ class USoundWave* URuntimeAudioImporterLibrary::GetSoundWaveObject(const uint8*
return sw;
}
bool URuntimeAudioImporterLibrary::TranscodeAudioFileToPCMData(const wchar_t* filePath, TEnumAsByte < AudioFormat > Format, TEnumAsByte < TranscodingStatus >& Status, uint64& framesToWrite, int16_t*& pSampleData, uint32& channels, uint32& sampleRate)
bool URuntimeAudioImporterLibrary::TranscodeAudioFileToPCMData(FString FilePath, TEnumAsByte<AudioFormat> Format,
TEnumAsByte<TranscodingStatus>& Status,
uint64& framesToWrite, int16_t*& pSampleData,
uint32& channels, uint32& sampleRate)
{
OnProgress_Internal(5);
switch (Format)
{
case AudioFormat::MP3:
{
drmp3_allocation_callbacks allocationCallbacksDecoding;
allocationCallbacksDecoding.pUserData = NULL;
allocationCallbacksDecoding.onMalloc = unreal_malloc;
allocationCallbacksDecoding.onRealloc = unreal_realloc;
allocationCallbacksDecoding.onFree = unreal_free;
{
drmp3_allocation_callbacks allocationCallbacksDecoding;
allocationCallbacksDecoding.pUserData = NULL;
allocationCallbacksDecoding.onMalloc = unreal_malloc;
allocationCallbacksDecoding.onRealloc = unreal_realloc;
allocationCallbacksDecoding.onFree = unreal_free;
drmp3 mp3;
if (!drmp3_init_file_w(&mp3, filePath, &allocationCallbacksDecoding)) {
Status = TranscodingStatus::FailedToOpenStream;
return false;
drmp3 mp3;
#if PLATFORM_WINDOWS
if (!drmp3_init_file_w(&mp3, (wchar_t*)FilePath.GetCharArray().GetData(), &allocationCallbacksDecoding))
#else
if (!drmp3_init_file(&mp3, TCHAR_TO_UTF8(*FilePath), &allocationCallbacksDecoding))
#endif
{
Status = TranscodingStatus::FailedToOpenStream;
return false;
}
else
{
// Callback Dispatcher OnProgress (not completely accurate implementation)
OnProgress_Internal(10);
pSampleData = (int16_t*)FMemory::Malloc(
(size_t)drmp3_get_pcm_frame_count(&mp3) * mp3.channels * sizeof(int16_t));
// Callback Dispatcher OnProgress (not completely accurate implementation)
OnProgress_Internal(15);
framesToWrite = drmp3_read_pcm_frames_s16(&mp3, drmp3_get_pcm_frame_count(&mp3), pSampleData);
// Callback Dispatcher OnProgress (not completely accurate implementation)
OnProgress_Internal(35);
channels = mp3.channels;
sampleRate = mp3.sampleRate;
drmp3_uninit(&mp3);
return true;
}
break;
}
else {
// Callback Dispatcher OnProgress (not completely accurate implementation)
OnProgress_Internal(10);
pSampleData = (int16_t*)FMemory::Malloc((size_t)drmp3_get_pcm_frame_count(&mp3) * mp3.channels * sizeof(int16_t));
// Callback Dispatcher OnProgress (not completely accurate implementation)
OnProgress_Internal(15);
framesToWrite = drmp3_read_pcm_frames_s16(&mp3, drmp3_get_pcm_frame_count(&mp3), pSampleData);
// Callback Dispatcher OnProgress (not completely accurate implementation)
OnProgress_Internal(35);
channels = mp3.channels;
sampleRate = mp3.sampleRate;
drmp3_uninit(&mp3);
return true;
}
break;
}
case AudioFormat::WAV:
{
drwav_allocation_callbacks allocationCallbacksDecoding;
allocationCallbacksDecoding.pUserData = NULL;
allocationCallbacksDecoding.onMalloc = unreal_malloc;
allocationCallbacksDecoding.onRealloc = unreal_realloc;
allocationCallbacksDecoding.onFree = unreal_free;
{
drwav_allocation_callbacks allocationCallbacksDecoding;
allocationCallbacksDecoding.pUserData = NULL;
allocationCallbacksDecoding.onMalloc = unreal_malloc;
allocationCallbacksDecoding.onRealloc = unreal_realloc;
allocationCallbacksDecoding.onFree = unreal_free;
drwav wav;
if (!drwav_init_file_w(&wav, filePath, &allocationCallbacksDecoding)) {
Status = TranscodingStatus::FailedToOpenStream;
return false;
drwav wav;
#if PLATFORM_WINDOWS
if (!drwav_init_file_w(&wav, (wchar_t*)FilePath.GetCharArray().GetData(), &allocationCallbacksDecoding))
#else
if (!drwav_init_file(&wav, TCHAR_TO_UTF8(*FilePath), &allocationCallbacksDecoding))
#endif
{
Status = TranscodingStatus::FailedToOpenStream;
return false;
}
else
{
// Callback Dispatcher OnProgress (not completely accurate implementation)
OnProgress_Internal(10);
pSampleData = (int16_t*)
FMemory::Malloc((size_t)wav.totalPCMFrameCount * wav.channels * sizeof(int16_t));
// Callback Dispatcher OnProgress (not completely accurate implementation)
OnProgress_Internal(15);
framesToWrite = drwav_read_pcm_frames_s16(&wav, wav.totalPCMFrameCount, pSampleData);
// Callback Dispatcher OnProgress (not completely accurate implementation)
OnProgress_Internal(35);
channels = wav.channels;
sampleRate = wav.sampleRate;
drwav_uninit(&wav);
return true;
}
break;
}
else {
// Callback Dispatcher OnProgress (not completely accurate implementation)
OnProgress_Internal(10);
pSampleData = (int16_t*)FMemory::Malloc((size_t)wav.totalPCMFrameCount * wav.channels * sizeof(int16_t));
// Callback Dispatcher OnProgress (not completely accurate implementation)
OnProgress_Internal(15);
framesToWrite = drwav_read_pcm_frames_s16(&wav, wav.totalPCMFrameCount, pSampleData);
// Callback Dispatcher OnProgress (not completely accurate implementation)
OnProgress_Internal(35);
channels = wav.channels;
sampleRate = wav.sampleRate;
drwav_uninit(&wav);
return true;
}
break;
}
case AudioFormat::FLAC:
{
drflac_allocation_callbacks allocationCallbacksDecoding;
allocationCallbacksDecoding.pUserData = NULL;
allocationCallbacksDecoding.onMalloc = unreal_malloc;
allocationCallbacksDecoding.onRealloc = unreal_realloc;
allocationCallbacksDecoding.onFree = unreal_free;
{
drflac_allocation_callbacks allocationCallbacksDecoding;
allocationCallbacksDecoding.pUserData = NULL;
allocationCallbacksDecoding.onMalloc = unreal_malloc;
allocationCallbacksDecoding.onRealloc = unreal_realloc;
allocationCallbacksDecoding.onFree = unreal_free;
drflac* pFlac = drflac_open_file_w(filePath, &allocationCallbacksDecoding);
if (pFlac == NULL) {
Status = TranscodingStatus::FailedToOpenStream;
return false;
#if PLATFORM_WINDOWS
drflac* pFlac = drflac_open_file_w((wchar_t*)FilePath.GetCharArray().GetData(),
&allocationCallbacksDecoding);
#else
drflac* pFlac = drflac_open_file(TCHAR_TO_UTF8(*FilePath),
&allocationCallbacksDecoding);
#endif
if (pFlac == NULL)
{
Status = TranscodingStatus::FailedToOpenStream;
return false;
}
else
{
// Callback Dispatcher OnProgress (not completely accurate implementation)
OnProgress_Internal(10);
pSampleData = (int16_t*)FMemory::Malloc(
(size_t)pFlac->totalPCMFrameCount * pFlac->channels * sizeof(int16_t));
// Callback Dispatcher OnProgress (not completely accurate implementation)
OnProgress_Internal(15);
framesToWrite = drflac_read_pcm_frames_s16(pFlac, pFlac->totalPCMFrameCount, pSampleData);
// Callback Dispatcher OnProgress (not completely accurate implementation)
OnProgress_Internal(35);
channels = pFlac->channels;
sampleRate = pFlac->sampleRate;
drflac_close(pFlac);
return true;
}
break;
}
else {
// Callback Dispatcher OnProgress (not completely accurate implementation)
OnProgress_Internal(10);
pSampleData = (int16_t*)FMemory::Malloc((size_t)pFlac->totalPCMFrameCount * pFlac->channels * sizeof(int16_t));
// Callback Dispatcher OnProgress (not completely accurate implementation)
OnProgress_Internal(15);
framesToWrite = drflac_read_pcm_frames_s16(pFlac, pFlac->totalPCMFrameCount, pSampleData);
// Callback Dispatcher OnProgress (not completely accurate implementation)
OnProgress_Internal(35);
channels = pFlac->channels;
sampleRate = pFlac->sampleRate;
drflac_close(pFlac);
return true;
}
break;
}
default:
{
Status = TranscodingStatus::InvalidAudioFormat;
return false;
break;
}
{
Status = TranscodingStatus::InvalidAudioFormat;
return false;
break;
}
}
}
bool URuntimeAudioImporterLibrary::TranscodePCMToWAVData(void*& WaveData, size_t& WaveDataSize, uint64 framesToWrite, int16_t*& pSampleData, uint32 channels, uint32 sampleRate) {
bool URuntimeAudioImporterLibrary::TranscodePCMToWAVData(void*& WaveData, size_t& WaveDataSize, uint64 framesToWrite,
int16_t*& pSampleData, uint32 channels, uint32 sampleRate)
{
drwav wavEncode;
drwav_data_format format;
format.container = drwav_container_riff;
@@ -386,7 +447,8 @@ bool URuntimeAudioImporterLibrary::TranscodePCMToWAVData(void*& WaveData, size_t
allocationCallbacks.onMalloc = unreal_malloc;
allocationCallbacks.onRealloc = unreal_realloc;
allocationCallbacks.onFree = unreal_free;
if (!drwav_init_memory_write(&wavEncode, &WaveData, &WaveDataSize, &format, &allocationCallbacks)) {
if (!drwav_init_memory_write(&wavEncode, &WaveData, &WaveDataSize, &format, &allocationCallbacks))
{
return false; //error
}
@@ -404,31 +466,37 @@ bool URuntimeAudioImporterLibrary::TranscodePCMToWAVData(void*& WaveData, size_t
TEnumAsByte<AudioFormat> URuntimeAudioImporterLibrary::GetAudioFormat(const FString& filePath)
{
if (FPaths::GetExtension(filePath, false).Equals(TEXT("mp3"), ESearchCase::IgnoreCase)) {
if (FPaths::GetExtension(filePath, false).Equals(TEXT("mp3"), ESearchCase::IgnoreCase))
{
return AudioFormat::MP3;
}
else if (FPaths::GetExtension(filePath, false).Equals(TEXT("wav"), ESearchCase::IgnoreCase)) {
else if (FPaths::GetExtension(filePath, false).Equals(TEXT("wav"), ESearchCase::IgnoreCase))
{
return AudioFormat::WAV;
}
else if (FPaths::GetExtension(filePath, false).Equals(TEXT("flac"), ESearchCase::IgnoreCase)) {
else if (FPaths::GetExtension(filePath, false).Equals(TEXT("flac"), ESearchCase::IgnoreCase))
{
return AudioFormat::FLAC;
}
else {
else
{
return AudioFormat::INVALID;
}
}
void URuntimeAudioImporterLibrary::OnProgress_Internal(int32 Percentage)
{
AsyncTask(ENamedThreads::GameThread, [=]() {
AsyncTask(ENamedThreads::GameThread, [=]()
{
OnProgress.Broadcast(Percentage);
});
});
}
void URuntimeAudioImporterLibrary::OnResult_Internal(USoundWave* ReadySoundWave, TEnumAsByte < TranscodingStatus > Status)
void URuntimeAudioImporterLibrary::OnResult_Internal(USoundWave* ReadySoundWave, TEnumAsByte<TranscodingStatus> Status)
{
AsyncTask(ENamedThreads::GameThread, [=]() {
AsyncTask(ENamedThreads::GameThread, [=]()
{
RemoveFromRoot();
OnResult.Broadcast(ReadySoundWave, Status);
});
});
}
@@ -11,18 +11,18 @@
UENUM(BlueprintType, Category = "RuntimeAudioImporter")
enum TranscodingStatus
{
SuccessTranscoding UMETA(DisplayName = "Success"),
FailedToOpenStream UMETA(DisplayName = "Failed to open stream"),
UnsupportedBitDepth UMETA(DisplayName = "Unsupported bit depth (Unreal Engine only supports 16 bit)"),
InvalidWAVEData UMETA(DisplayName = "Invalid WAVE data"),
InvalidBitrate UMETA(DisplayName = "Invalid bitrate (Actual bit rate does not match the container size)"),
InvalidSubformat UMETA(DisplayName = "InvalidSubformat (Subformat identifier not recognized)"),
UnrecognizedReadWaveError UMETA(DisplayName = "Unrecognized ReadWave error"),
USoundWaveDeclarationError UMETA(DisplayName = "USoundWave declaration error"),
InvalidUSoundWave UMETA(DisplayName = "Invalid USoundWave (The USoundWave object contains incorrect data)"),
InvalidAudioFormat UMETA(DisplayName = "Invalid audio format (Can't determine the format of the audio file)"),
AudioDoesNotExist UMETA(DisplayName = "The audio file does not exist"),
ZeroDurationError UMETA(DisplayName = "Transcoded audio has zero length")
SuccessTranscoding UMETA(DisplayName = "Success"),
FailedToOpenStream UMETA(DisplayName = "Failed to open stream"),
UnsupportedBitDepth UMETA(DisplayName = "Unsupported bit depth (Unreal Engine only supports 16 bit)"),
InvalidWAVEData UMETA(DisplayName = "Invalid WAVE data"),
InvalidBitrate UMETA(DisplayName = "Invalid bitrate (Actual bit rate does not match the container size)"),
InvalidSubformat UMETA(DisplayName = "InvalidSubformat (Subformat identifier not recognized)"),
UnrecognizedReadWaveError UMETA(DisplayName = "Unrecognized ReadWave error"),
USoundWaveDeclarationError UMETA(DisplayName = "USoundWave declaration error"),
InvalidUSoundWave UMETA(DisplayName = "Invalid USoundWave (The USoundWave object contains incorrect data)"),
InvalidAudioFormat UMETA(DisplayName = "Invalid audio format (Can't determine the format of the audio file)"),
AudioDoesNotExist UMETA(DisplayName = "The audio file does not exist"),
ZeroDurationError UMETA(DisplayName = "Transcoded audio has zero length")
};
/**
@@ -31,10 +31,10 @@ enum TranscodingStatus
UENUM(BlueprintType, Category = "RuntimeAudioImporter")
enum AudioFormat
{
MP3 UMETA(DisplayName = "mp3"),
WAV UMETA(DisplayName = "wav"),
FLAC UMETA(DisplayName = "flac"),
INVALID UMETA(DisplayName = "invalid (not defined format, internal use only)", Hidden)
MP3 UMETA(DisplayName = "mp3"),
WAV UMETA(DisplayName = "wav"),
FLAC UMETA(DisplayName = "flac"),
INVALID UMETA(DisplayName = "invalid (not defined format, internal use only)", Hidden)
};
/**
@@ -51,7 +51,8 @@ DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnProgress, const int32, Percentage
* @param ReadySoundWave Ready USoundWave Object
* @param Status Reference to TranscodingStatus Enumerator in case an error occurs
*/
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnResult, const USoundWave*, ReadySoundWave, const TEnumAsByte < TranscodingStatus >, Status);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnResult, const USoundWave*, ReadySoundWave,
const TEnumAsByte < TranscodingStatus >, Status);
UCLASS(BlueprintType, Category = "RuntimeFilesDownloader")
class RUNTIMEAUDIOIMPORTER_API URuntimeAudioImporterLibrary : public UObject
@@ -62,32 +63,35 @@ public:
* Bind to know when the transcoding is on progress.
*/
UPROPERTY(BlueprintAssignable, Category = "RuntimeAudioImporter")
FOnProgress OnProgress;
FOnProgress OnProgress;
/**
* Bind to know when the transcoding is complete (even if it fails).
*/
UPROPERTY(BlueprintAssignable, Category = "RuntimeAudioImporter")
FOnResult OnResult;
FOnResult OnResult;
/**
* Instantiates a RuntimeAudioImporter object.
*
* @return The RuntimeAudioImporter object. Bind to it's OnProgress and OnResult events to know when it is in the process of transcoding and has been transcoded.
*/
UFUNCTION(BlueprintCallable, meta = (Keywords = "Create, Audio, Runtime, MP3, FLAC, WAV"), Category = "RuntimeAudioImporter")
static URuntimeAudioImporterLibrary * CreateRuntimeAudioImporter();
UFUNCTION(BlueprintCallable, meta = (Keywords = "Create, Audio, Runtime, MP3, FLAC, WAV"), Category =
"RuntimeAudioImporter")
static URuntimeAudioImporterLibrary* CreateRuntimeAudioImporter();
/**
* Transcode audio file to USoundWave static object
*
* @param filePath Path to the audio file to import
* @param FilePath Path to the audio file to import
* @param Format Audio file format (extension)
* @param DefineFormatAutomatically Whether to define format (extension) automatically or not
* @return Returns itself.
*/
UFUNCTION(BlueprintCallable, meta = (Keywords = "Importer, Transcoder, Converter, Runtime, MP3, FLAC, WAV"), Category = "RuntimeAudioImporter")
URuntimeAudioImporterLibrary * ImportAudioFromFile(const FString & filePath, TEnumAsByte < AudioFormat > Format, bool DefineFormatAutomatically);
UFUNCTION(BlueprintCallable, meta = (Keywords = "Importer, Transcoder, Converter, Runtime, MP3, FLAC, WAV"),
Category = "RuntimeAudioImporter")
URuntimeAudioImporterLibrary* ImportAudioFromFile(const FString& FilePath, TEnumAsByte<AudioFormat> Format,
bool DefineFormatAutomatically);
/**
* Destroy USoundWave static object. After the Soundwave is no longer needed, you need to call this function to free memory
@@ -96,7 +100,7 @@ public:
* @return Whether the descruction of USoundWave was successful or not
*/
UFUNCTION(BlueprintCallable, meta = (Keywords = "MP3, FLAC, WAV, Destroy"), Category = "RuntimeAudioImporter")
static bool DestroySoundWave(USoundWave * ReadySoundWave);
static bool DestroySoundWave(USoundWave* ReadySoundWave);
/**
* Export (save) SoundWave object to .WAV audio file to device memory. So far beta and it is not recommended to use due to the fact that the engine can clear the sound data itself
@@ -106,19 +110,22 @@ public:
* @return Whether the save was successfull or not
* @warning It is very important that USoundWave should not used by the engine so that it can be read from physical memory.
*/
UFUNCTION(BlueprintCallable, meta = (Keywords = "Exporter, Export, Save, Runtime, WAV"), Category = "RuntimeAudioImporter")
static bool ExportSoundWaveToFile(USoundWave * SoundWaveToExport, FString PathToExport);
UFUNCTION(BlueprintCallable, meta = (Keywords = "Exporter, Export, Save, Runtime, WAV"), Category =
"RuntimeAudioImporter")
static bool ExportSoundWaveToFile(USoundWave* SoundWaveToExport, FString PathToExport);
private:
/**
* Internal main audio transcoding function
*
* @param filePath Path to the audio file to import
* @param FilePath Path to the audio file to import
* @param Format Audio file format (extension)
* @param DefineFormatAutomatically Whether to define format (extension) automatically or not
* @return Returns itself
*/
URuntimeAudioImporterLibrary * GetUSoundWaveFromAudioFile_Internal(const FString & filePath, TEnumAsByte < AudioFormat > Format, bool DefineFormatAutomatically);
URuntimeAudioImporterLibrary* GetUSoundWaveFromAudioFile_Internal(const FString& FilePath,
TEnumAsByte<AudioFormat> Format,
bool DefineFormatAutomatically);
/**
* Get USoundWave static object reference from 16-bit WAV data
@@ -128,12 +135,13 @@ private:
* @param Status Reference to TranscodingStatus Enumerator in case an error occurs
* @return Returns a ready USoundWave object
*/
class USoundWave* GetSoundWaveObject(const uint8 * WaveData, int32 WaveDataSize, TEnumAsByte < TranscodingStatus > & Status);
class USoundWave* GetSoundWaveObject(const uint8* WaveData, int32 WaveDataSize,
TEnumAsByte<TranscodingStatus>& Status);
/**
* Transcode Audio from File to PCM Data
*
* @param filePath Path to the audio file from which to receive PCM data
* @param FilePath Path to the audio file from which to receive PCM data
* @param Format Format of the audio file (e.g. mp3. flac, etc)
* @param Status Reference to TranscodingStatus Enumerator in case an error occurs
* @param framesToWrite PCM frames ready to be written
@@ -142,7 +150,9 @@ private:
* @param sampleRate Sample rate of the audio
* @return Whether the transcoding was successful or not
*/
bool TranscodeAudioFileToPCMData(const wchar_t* filePath, TEnumAsByte < AudioFormat > Format, TEnumAsByte < TranscodingStatus > & Status, uint64 & framesToWrite, int16_t * &pSampleData, uint32 & channels, uint32 & sampleRate);
bool TranscodeAudioFileToPCMData(FString FilePath, TEnumAsByte<AudioFormat> Format,
TEnumAsByte<TranscodingStatus>& Status, uint64& framesToWrite,
int16_t* & pSampleData, uint32& channels, uint32& sampleRate);
/**
* Transcode Audio from PCM to 16-bit WAV data
@@ -155,13 +165,14 @@ private:
* @param sampleRate Sample rate of the audio
* @return Whether the transcoding was successful or not
*/
bool TranscodePCMToWAVData(void*& WaveData, size_t & WaveDataSize, uint64 framesToWrite, int16_t * &pSampleData, uint32 channels, uint32 sampleRate);
bool TranscodePCMToWAVData(void*& WaveData, size_t& WaveDataSize, uint64 framesToWrite, int16_t* & pSampleData,
uint32 channels, uint32 sampleRate);
/**
* @param filePath File path, where to find the format (by extension)
* @param FilePath File path, where to find the format (by extension)
* @return Returns the found audio format (e.g. mp3. flac, etc) by AudioFormat Enumerator
*/
static TEnumAsByte<AudioFormat> GetAudioFormat(const FString & filePath);
static TEnumAsByte<AudioFormat> GetAudioFormat(const FString& FilePath);
/**
* Audio transcoding progress callback
@@ -175,5 +186,5 @@ private:
* @param ReadySoundWave A ready USoundWave object
* @param Status Reference to TranscodingStatus Enumerator
*/
void OnResult_Internal(USoundWave * ReadySoundWave, TEnumAsByte < TranscodingStatus > Status);
void OnResult_Internal(USoundWave* ReadySoundWave, TEnumAsByte<TranscodingStatus> Status);
};