mirror of
https://github.com/storytold/RuntimeAudioImporter.git
synced 2026-10-09 00:09:48 +00:00
big update: callback dispatchers, memory leak fixes and more
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
@@ -4,37 +4,21 @@
|
||||
"VersionName": "1.0",
|
||||
"FriendlyName": "RuntimeAudioImporter",
|
||||
"Description": "Runtime Audio Importer plugin for Unreal Engine 4. Supports mp3, wav and flac formats.",
|
||||
"Category": "Code Plugins",
|
||||
"Category": "Audio",
|
||||
"CreatedBy": "Respirant",
|
||||
"CreatedByURL": "https://github.com/Respirant/RuntimeAudioImporter",
|
||||
"DocsURL": "https://github.com/Respirant/RuntimeAudioImporter",
|
||||
"MarketplaceURL": "com.epicgames.launcher://ue/marketplace/product/81fd278e318e4235a028d3fdf46bf036",
|
||||
"MarketplaceURL": "",
|
||||
"SupportURL": "https://telegram.me/Respirant",
|
||||
"CanContainContent": false,
|
||||
"IsBetaVersion": false,
|
||||
"IsExperimentalVersion": false,
|
||||
"Installed": true,
|
||||
"EngineVersion": "4.26.0",
|
||||
"Modules": [
|
||||
{
|
||||
"Name": "RuntimeAudioImporter",
|
||||
"Type": "Runtime",
|
||||
"LoadingPhase": "Default",
|
||||
"WhitelistPlatforms": [
|
||||
"Win64",
|
||||
"Win32",
|
||||
"Mac",
|
||||
"Android",
|
||||
"IOS",
|
||||
"Linux"
|
||||
],
|
||||
"BlacklistPlatforms": [
|
||||
"XboxOne",
|
||||
"PS4",
|
||||
"HTML5",
|
||||
"TVOS",
|
||||
"Switch"
|
||||
]
|
||||
"LoadingPhase": "Default"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
-248
@@ -1,248 +0,0 @@
|
||||
// Respirant 2020.
|
||||
|
||||
#include "RuntimeAudioImporterBPLibrary.h"
|
||||
#include "RuntimeAudioImporter.h"
|
||||
|
||||
URuntimeAudioImporterBPLibrary::URuntimeAudioImporterBPLibrary(const FObjectInitializer& ObjectInitializer)
|
||||
: Super(ObjectInitializer)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Transcode audio file to USoundWave static object
|
||||
*
|
||||
* @param filePath Path to the audio file to import
|
||||
* @param Format Audio file format (extension)
|
||||
* @param status Final import status (TranscodingStatus)
|
||||
* @param DefineFormatAutomatically Whether to define format (extension) automatically or not
|
||||
* @return Returns USoundWave Static Object.
|
||||
*/
|
||||
class USoundWave* URuntimeAudioImporterBPLibrary::GetSoundWaveFromAudioFile(const FString& filePath, TEnumAsByte < AudioFormat > Format, TEnumAsByte < TranscodingStatus >& status, bool DefineFormatAutomatically)
|
||||
{
|
||||
return GetUSoundWaveFromAudioFile_Internal(filePath, Format, status, DefineFormatAutomatically);
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy USoundWave static object
|
||||
*
|
||||
* @return Returns true - success, false - failure.
|
||||
*/
|
||||
bool URuntimeAudioImporterBPLibrary::DestroySoundWave(USoundWave* ReadySoundWave) {
|
||||
|
||||
if (IsValid(ReadySoundWave) && ReadySoundWave->IsValidLowLevel()) {
|
||||
|
||||
/* Free memory */
|
||||
ReadySoundWave->InvalidateCompressedData();
|
||||
FMemory::Free(ReadySoundWave->RawPCMData);
|
||||
ReadySoundWave->RawData.RemoveBulkData();
|
||||
ReadySoundWave->RemoveAudioResource();
|
||||
/* Free memory */
|
||||
|
||||
/* Destroying USoundWave object as UObject */
|
||||
ReadySoundWave->ConditionalBeginDestroy();
|
||||
ReadySoundWave = NULL;
|
||||
/* Destroying USoundWave object as UObject */
|
||||
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Get USoundWave static object reference */
|
||||
class USoundWave* URuntimeAudioImporterBPLibrary::GetSoundWaveObject(const uint8* WaveData, int32 WaveDataSize, TEnumAsByte < TranscodingStatus >& status) {
|
||||
USoundWave* sw = NewObject<USoundWave>(USoundWave::StaticClass());
|
||||
if (!sw) {
|
||||
status = TranscodingStatus::USoundWaveDeclarationError;
|
||||
return nullptr;
|
||||
}
|
||||
FWaveModInfo WaveInfo;
|
||||
FString ErrorReason;
|
||||
if (WaveInfo.ReadWaveInfo(WaveData, WaveDataSize, &ErrorReason))
|
||||
{
|
||||
if (*WaveInfo.pBitsPerSample != 16) {
|
||||
status = TranscodingStatus::UnsupportedBitDepth;
|
||||
return nullptr;
|
||||
}
|
||||
sw->InvalidateCompressedData();
|
||||
sw->RawData.Lock(LOCK_READ_WRITE);
|
||||
void* LockedData = sw->RawData.Realloc(WaveDataSize);
|
||||
FMemory::Memcpy(LockedData, WaveData, WaveDataSize);
|
||||
sw->RawData.Unlock();
|
||||
|
||||
int32 DurationDiv = *WaveInfo.pChannels * *WaveInfo.pBitsPerSample * *WaveInfo.pSamplesPerSec;
|
||||
if (DurationDiv)
|
||||
{
|
||||
sw->Duration = *WaveInfo.pWaveDataSize * 8.0f / DurationDiv;
|
||||
}
|
||||
else
|
||||
{
|
||||
sw->Duration = 0.0f;
|
||||
status = TranscodingStatus::ZeroDurationError;
|
||||
return nullptr;
|
||||
}
|
||||
sw->SetSampleRate(*WaveInfo.pSamplesPerSec);
|
||||
sw->NumChannels = *WaveInfo.pChannels;
|
||||
sw->RawPCMDataSize = WaveInfo.SampleDataSize;
|
||||
sw->SoundGroup = ESoundGroup::SOUNDGROUP_Default;
|
||||
}
|
||||
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)) {
|
||||
status = TranscodingStatus::InvalidBitrate;
|
||||
}
|
||||
else if (ErrorReason.Equals(TEXT("Unsupported WAVE file format: subformat identifier not recognized."), ESearchCase::CaseSensitive)) {
|
||||
status = TranscodingStatus::InvalidBitrate;
|
||||
}
|
||||
else {
|
||||
status = TranscodingStatus::UnrecognizedReadWaveError;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
sw->RawPCMData = (uint8*)FMemory::Malloc(sw->RawPCMDataSize);
|
||||
FMemory::Memcpy(sw->RawPCMData, WaveInfo.SampleDataStart, sw->RawPCMDataSize);
|
||||
|
||||
if (!sw) {
|
||||
status = TranscodingStatus::InvalidUSoundWave;
|
||||
return nullptr;
|
||||
}
|
||||
status = TranscodingStatus::SuccessTranscoding;
|
||||
return sw;
|
||||
}
|
||||
|
||||
#include "ThirdParty/dr_wav.h"
|
||||
|
||||
#include "ThirdParty/dr_mp3.h"
|
||||
|
||||
#include "ThirdParty/dr_flac.h"
|
||||
|
||||
/* Main internal get USoundWave from audio file */
|
||||
class USoundWave* URuntimeAudioImporterBPLibrary::GetUSoundWaveFromAudioFile_Internal(const FString& filePath, TEnumAsByte < AudioFormat > Format, TEnumAsByte < TranscodingStatus >& status, bool DefineFormatAutomatically)
|
||||
{
|
||||
if (FPaths::FileExists(filePath) != true) {
|
||||
status = TranscodingStatus::AudioDoesNotExist;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int16_t* pSampleData;
|
||||
uint64 framesToWrite;
|
||||
uint32 channels;
|
||||
uint32 sampleRate;
|
||||
|
||||
if (DefineFormatAutomatically) {
|
||||
Format = GetAudioFormat(filePath);
|
||||
if (Format == AudioFormat::INVALID) {
|
||||
status = TranscodingStatus::InvalidAudioFormat;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
if (TranscodeAudioToWaveData(TCHAR_TO_ANSI(*filePath), Format, status, framesToWrite, pSampleData, channels, sampleRate) == false) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
drwav wavEncode;
|
||||
drwav_data_format format;
|
||||
format.container = drwav_container_riff;
|
||||
format.format = DR_WAVE_FORMAT_PCM;
|
||||
format.channels = channels;
|
||||
format.sampleRate = sampleRate;
|
||||
format.bitsPerSample = 16; // Unreal Engine 4 supports only 16 bit rate
|
||||
|
||||
void* pWavFileData;
|
||||
size_t wavFileSize;
|
||||
drwav_init_memory_write(&wavEncode, &pWavFileData, &wavFileSize, &format, NULL);
|
||||
drwav_write_pcm_frames(&wavEncode, framesToWrite, pSampleData);
|
||||
drwav_uninit(&wavEncode);
|
||||
|
||||
USoundWave* ReadyUSoundWave = GetSoundWaveObject((const uint8*)pWavFileData, wavFileSize, status); // A ready USoundWave static object
|
||||
|
||||
drwav_free(pSampleData, NULL);
|
||||
drwav_free(pWavFileData, NULL);
|
||||
|
||||
return ReadyUSoundWave;
|
||||
}
|
||||
|
||||
/* Automatically get audio format. Internal use only recommended */
|
||||
TEnumAsByte<AudioFormat> URuntimeAudioImporterBPLibrary::GetAudioFormat(const FString& filePath) {
|
||||
if (FPaths::GetExtension(filePath, false).Equals(TEXT("mp3"), ESearchCase::IgnoreCase)) {
|
||||
return AudioFormat::MP3;
|
||||
}
|
||||
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)) {
|
||||
return AudioFormat::FLAC;
|
||||
}
|
||||
else {
|
||||
return AudioFormat::INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
/* Transcode Audio to Wave Data */
|
||||
bool URuntimeAudioImporterBPLibrary::TranscodeAudioToWaveData(const char* filePath, TEnumAsByte<AudioFormat> Format, TEnumAsByte<TranscodingStatus>& status, uint64& framesToWrite, int16_t*& pSampleData, uint32& channels, uint32& sampleRate) {
|
||||
switch (Format)
|
||||
{
|
||||
case AudioFormat::MP3:
|
||||
{
|
||||
drmp3 mp3;
|
||||
if (!drmp3_init_file(&mp3, filePath, NULL)) {
|
||||
status = TranscodingStatus::FailedToOpenStream;
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
pSampleData = (int16_t*)malloc((size_t)drmp3_get_pcm_frame_count(&mp3) * mp3.channels * sizeof(int16_t));
|
||||
framesToWrite = drmp3_read_pcm_frames_s16(&mp3, drmp3_get_pcm_frame_count(&mp3), pSampleData);
|
||||
channels = mp3.channels;
|
||||
sampleRate = mp3.sampleRate;
|
||||
drmp3_uninit(&mp3);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case AudioFormat::WAV:
|
||||
{
|
||||
drwav wav;
|
||||
if (!drwav_init_file(&wav, filePath, NULL)) {
|
||||
status = TranscodingStatus::FailedToOpenStream;
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
pSampleData = (int16_t*)malloc((size_t)wav.totalPCMFrameCount * wav.channels * sizeof(int16_t));
|
||||
framesToWrite = drwav_read_pcm_frames_s16(&wav, wav.totalPCMFrameCount, pSampleData);
|
||||
channels = wav.channels;
|
||||
sampleRate = wav.sampleRate;
|
||||
drwav_uninit(&wav);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case AudioFormat::FLAC:
|
||||
{
|
||||
drflac* pFlac = drflac_open_file(filePath, NULL);
|
||||
if (pFlac == NULL) {
|
||||
status = TranscodingStatus::FailedToOpenStream;
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
pSampleData = (int16_t*)malloc((size_t)pFlac->totalPCMFrameCount * pFlac->channels * sizeof(int16_t));
|
||||
framesToWrite = drflac_read_pcm_frames_s16(pFlac, pFlac->totalPCMFrameCount, pSampleData);
|
||||
channels = pFlac->channels;
|
||||
sampleRate = pFlac->sampleRate;
|
||||
drflac_close(pFlac);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
status = TranscodingStatus::InvalidAudioFormat;
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
+405
@@ -0,0 +1,405 @@
|
||||
// Respirant 2020.
|
||||
|
||||
#include "RuntimeAudioImporterLibrary.h"
|
||||
#include "RuntimeAudioImporter.h"
|
||||
|
||||
URuntimeAudioImporterLibrary::URuntimeAudioImporterLibrary(const FObjectInitializer& ObjectInitializer)
|
||||
: Super(ObjectInitializer)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
URuntimeAudioImporterLibrary::~URuntimeAudioImporterLibrary()
|
||||
{
|
||||
}
|
||||
|
||||
URuntimeAudioImporterLibrary* URuntimeAudioImporterLibrary::CreateRuntimeAudioImporter()
|
||||
{
|
||||
URuntimeAudioImporterLibrary* Importer = NewObject<URuntimeAudioImporterLibrary>();
|
||||
return Importer;
|
||||
}
|
||||
|
||||
URuntimeAudioImporterLibrary* URuntimeAudioImporterLibrary::ImportAudioFromFile(const FString& filePath, TEnumAsByte < AudioFormat > Format, bool DefineFormatAutomatically)
|
||||
{
|
||||
AddToRoot();
|
||||
AsyncTask(ENamedThreads::AnyThread, [=]() {
|
||||
GetUSoundWaveFromAudioFile_Internal(filePath, Format, DefineFormatAutomatically);
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
bool URuntimeAudioImporterLibrary::DestroySoundWave(USoundWave* ReadySoundWave) {
|
||||
|
||||
if (ReadySoundWave->IsValidLowLevel()) {
|
||||
|
||||
//Begin USoundWave destroy (block changing some data)
|
||||
ReadySoundWave->ConditionalBeginDestroy();
|
||||
ReadySoundWave->BeginDestroy();
|
||||
|
||||
//Delete all audio data from physical memory
|
||||
ReadySoundWave->RawData.RemoveBulkData();
|
||||
ReadySoundWave->RemoveAudioResource();
|
||||
ReadySoundWave->InvalidateCompressedData(true);
|
||||
|
||||
//Finish USoundWave destroy (complete object deletion as UObject)
|
||||
if (ReadySoundWave->IsReadyForFinishDestroy()) {
|
||||
ReadySoundWave->ConditionalFinishDestroy();
|
||||
ReadySoundWave->FinishDestroy();
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Including dr_wav, dr_mp3 and dr_flac libraries
|
||||
*/
|
||||
#include "ThirdParty/dr_wav.h"
|
||||
#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.
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
URuntimeAudioImporterLibrary* URuntimeAudioImporterLibrary::GetUSoundWaveFromAudioFile_Internal(const FString& filePath, TEnumAsByte < AudioFormat > Format, bool DefineFormatAutomatically)
|
||||
{
|
||||
TEnumAsByte < TranscodingStatus > Status;
|
||||
if (FPaths::FileExists(filePath) != true) {
|
||||
Status = TranscodingStatus::AudioDoesNotExist;
|
||||
|
||||
// Callback Dispatcher OnResult
|
||||
OnResult_Internal(nullptr, Status);
|
||||
}
|
||||
else {
|
||||
int16_t* pSampleData;
|
||||
uint64 framesToWrite;
|
||||
uint32 channels;
|
||||
uint32 sampleRate;
|
||||
|
||||
if (DefineFormatAutomatically) {
|
||||
Format = GetAudioFormat(filePath);
|
||||
}
|
||||
|
||||
if (Format == AudioFormat::INVALID) {
|
||||
Status = TranscodingStatus::InvalidAudioFormat;
|
||||
|
||||
// Callback Dispatcher OnResult
|
||||
OnResult_Internal(nullptr, Status);
|
||||
}
|
||||
else {
|
||||
if (TranscodeAudioFileToPCMData(this, TCHAR_TO_ANSI(*filePath), Format, Status, framesToWrite, pSampleData, channels, sampleRate) == false) {
|
||||
FMemory::Free(pSampleData);
|
||||
|
||||
// Callback Dispatcher OnResult
|
||||
OnResult_Internal(nullptr, Status);
|
||||
}
|
||||
else {
|
||||
void* WaveData;
|
||||
size_t WaveDataSize;
|
||||
|
||||
if (TranscodePCMToWAVData(this, *&WaveData, *&WaveDataSize, framesToWrite, *&pSampleData, channels, sampleRate) == false) {
|
||||
FMemory::Free(pSampleData);
|
||||
|
||||
// Callback Dispatcher OnResult
|
||||
OnResult_Internal(nullptr, Status);
|
||||
}
|
||||
else {
|
||||
USoundWave* ReadyUSoundWave = GetSoundWaveObject(this, (const uint8*)WaveData, WaveDataSize, Status); // A ready USoundWave static object
|
||||
|
||||
/* Free temporary transcoding data */
|
||||
FMemory::Free(WaveData);
|
||||
FMemory::Free(pSampleData);
|
||||
/* Free temporary transcoding data */
|
||||
|
||||
// Callback Dispatcher OnProgress (not completely accurate implementation)
|
||||
OnProgress_Internal(100);
|
||||
|
||||
// Callback Dispatcher OnResult
|
||||
OnResult_Internal(ReadyUSoundWave, Status);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
class USoundWave* URuntimeAudioImporterLibrary::GetSoundWaveObject(URuntimeAudioImporterLibrary* RuntimeAudioImporterObjRef, const uint8* WaveData, int32 WaveDataSize, TEnumAsByte < TranscodingStatus >& Status)
|
||||
{
|
||||
USoundWave* sw = NewObject<USoundWave>(USoundWave::StaticClass());
|
||||
if (!sw) {
|
||||
Status = TranscodingStatus::USoundWaveDeclarationError;
|
||||
return nullptr;
|
||||
}
|
||||
FWaveModInfo WaveInfo;
|
||||
FString ErrorReason;
|
||||
|
||||
// Callback Dispatcher OnProgress (not completely accurate implementation)
|
||||
RuntimeAudioImporterObjRef->OnProgress_Internal(65);
|
||||
|
||||
if (WaveInfo.ReadWaveInfo(WaveData, WaveDataSize, &ErrorReason))
|
||||
{
|
||||
// Callback Dispatcher OnProgress (not completely accurate implementation)
|
||||
RuntimeAudioImporterObjRef->OnProgress_Internal(75);
|
||||
|
||||
if (*WaveInfo.pBitsPerSample != 16) {
|
||||
Status = TranscodingStatus::UnsupportedBitDepth;
|
||||
return nullptr;
|
||||
}
|
||||
sw->InvalidateCompressedData();
|
||||
sw->RawData.Lock(LOCK_READ_WRITE);
|
||||
void* LockedData = sw->RawData.Realloc(WaveDataSize);
|
||||
FMemory::Memcpy(LockedData, WaveData, WaveDataSize);
|
||||
sw->RawData.Unlock();
|
||||
|
||||
// Callback Dispatcher OnProgress (not completely accurate implementation)
|
||||
RuntimeAudioImporterObjRef->OnProgress_Internal(80);
|
||||
|
||||
int32 DurationDiv = *WaveInfo.pChannels * *WaveInfo.pBitsPerSample * *WaveInfo.pSamplesPerSec;
|
||||
if (DurationDiv)
|
||||
{
|
||||
sw->Duration = *WaveInfo.pWaveDataSize * 8.0f / DurationDiv;
|
||||
}
|
||||
else
|
||||
{
|
||||
sw->Duration = 0.0f;
|
||||
Status = TranscodingStatus::ZeroDurationError;
|
||||
return nullptr;
|
||||
}
|
||||
sw->SetSampleRate(*WaveInfo.pSamplesPerSec);
|
||||
sw->NumChannels = *WaveInfo.pChannels;
|
||||
sw->RawPCMDataSize = WaveInfo.SampleDataSize;
|
||||
sw->SoundGroup = ESoundGroup::SOUNDGROUP_Default;
|
||||
|
||||
// Callback Dispatcher OnProgress (not completely accurate implementation)
|
||||
RuntimeAudioImporterObjRef->OnProgress_Internal(85);
|
||||
}
|
||||
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)) {
|
||||
Status = TranscodingStatus::InvalidBitrate;
|
||||
}
|
||||
else if (ErrorReason.Equals(TEXT("Unsupported WAVE file format: subformat identifier not recognized."), ESearchCase::CaseSensitive)) {
|
||||
Status = TranscodingStatus::InvalidBitrate;
|
||||
}
|
||||
else {
|
||||
Status = TranscodingStatus::UnrecognizedReadWaveError;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
sw->RawPCMData = (uint8*)FMemory::Malloc(sw->RawPCMDataSize);
|
||||
|
||||
// Callback Dispatcher OnProgress (not completely accurate implementation)
|
||||
RuntimeAudioImporterObjRef->OnProgress_Internal(90);
|
||||
|
||||
FMemory::Memcpy(sw->RawPCMData, WaveInfo.SampleDataStart, sw->RawPCMDataSize);
|
||||
|
||||
// Callback Dispatcher OnProgress (not completely accurate implementation)
|
||||
RuntimeAudioImporterObjRef->OnProgress_Internal(95);
|
||||
|
||||
if (!sw) {
|
||||
Status = TranscodingStatus::InvalidUSoundWave;
|
||||
return nullptr;
|
||||
}
|
||||
Status = TranscodingStatus::SuccessTranscoding;
|
||||
return sw;
|
||||
}
|
||||
|
||||
bool URuntimeAudioImporterLibrary::TranscodeAudioFileToPCMData(URuntimeAudioImporterLibrary* RuntimeAudioImporterObjRef, const char* filePath, TEnumAsByte < AudioFormat > Format, TEnumAsByte < TranscodingStatus >& Status, uint64& framesToWrite, int16_t*& pSampleData, uint32& channels, uint32& sampleRate)
|
||||
{
|
||||
RuntimeAudioImporterObjRef->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 mp3;
|
||||
if (!drmp3_init_file(&mp3, filePath, &allocationCallbacksDecoding)) {
|
||||
Status = TranscodingStatus::FailedToOpenStream;
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
// Callback Dispatcher OnProgress (not completely accurate implementation)
|
||||
RuntimeAudioImporterObjRef->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)
|
||||
RuntimeAudioImporterObjRef->OnProgress_Internal(15);
|
||||
|
||||
framesToWrite = drmp3_read_pcm_frames_s16(&mp3, drmp3_get_pcm_frame_count(&mp3), pSampleData);
|
||||
|
||||
// Callback Dispatcher OnProgress (not completely accurate implementation)
|
||||
RuntimeAudioImporterObjRef->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 wav;
|
||||
if (!drwav_init_file(&wav, filePath, &allocationCallbacksDecoding)) {
|
||||
Status = TranscodingStatus::FailedToOpenStream;
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
// Callback Dispatcher OnProgress (not completely accurate implementation)
|
||||
RuntimeAudioImporterObjRef->OnProgress_Internal(10);
|
||||
|
||||
pSampleData = (int16_t*)FMemory::Malloc((size_t)wav.totalPCMFrameCount * wav.channels * sizeof(int16_t));
|
||||
|
||||
// Callback Dispatcher OnProgress (not completely accurate implementation)
|
||||
RuntimeAudioImporterObjRef->OnProgress_Internal(15);
|
||||
|
||||
framesToWrite = drwav_read_pcm_frames_s16(&wav, wav.totalPCMFrameCount, pSampleData);
|
||||
|
||||
// Callback Dispatcher OnProgress (not completely accurate implementation)
|
||||
RuntimeAudioImporterObjRef->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* pFlac = drflac_open_file(filePath, &allocationCallbacksDecoding);
|
||||
if (pFlac == NULL) {
|
||||
Status = TranscodingStatus::FailedToOpenStream;
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
// Callback Dispatcher OnProgress (not completely accurate implementation)
|
||||
RuntimeAudioImporterObjRef->OnProgress_Internal(10);
|
||||
|
||||
pSampleData = (int16_t*)FMemory::Malloc((size_t)pFlac->totalPCMFrameCount * pFlac->channels * sizeof(int16_t));
|
||||
|
||||
// Callback Dispatcher OnProgress (not completely accurate implementation)
|
||||
RuntimeAudioImporterObjRef->OnProgress_Internal(15);
|
||||
|
||||
framesToWrite = drflac_read_pcm_frames_s16(pFlac, pFlac->totalPCMFrameCount, pSampleData);
|
||||
|
||||
// Callback Dispatcher OnProgress (not completely accurate implementation)
|
||||
RuntimeAudioImporterObjRef->OnProgress_Internal(35);
|
||||
|
||||
channels = pFlac->channels;
|
||||
sampleRate = pFlac->sampleRate;
|
||||
drflac_close(pFlac);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
Status = TranscodingStatus::InvalidAudioFormat;
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool URuntimeAudioImporterLibrary::TranscodePCMToWAVData(URuntimeAudioImporterLibrary* RuntimeAudioImporterObjRef, 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;
|
||||
format.format = DR_WAVE_FORMAT_PCM;
|
||||
format.channels = channels;
|
||||
format.sampleRate = sampleRate;
|
||||
format.bitsPerSample = 16; // Unreal Engine supports only 16-bit rate
|
||||
|
||||
drwav_allocation_callbacks allocationCallbacks;
|
||||
allocationCallbacks.pUserData = NULL;
|
||||
allocationCallbacks.onMalloc = unreal_malloc;
|
||||
allocationCallbacks.onRealloc = unreal_realloc;
|
||||
allocationCallbacks.onFree = unreal_free;
|
||||
if (!drwav_init_memory_write(&wavEncode, &WaveData, &WaveDataSize, &format, &allocationCallbacks)) {
|
||||
return false; //error
|
||||
}
|
||||
|
||||
// Callback Dispatcher OnProgress (not completely accurate implementation)
|
||||
RuntimeAudioImporterObjRef->OnProgress_Internal(40);
|
||||
|
||||
drwav_write_pcm_frames(&wavEncode, framesToWrite, pSampleData);
|
||||
|
||||
// Callback Dispatcher OnProgress (not completely accurate implementation)
|
||||
RuntimeAudioImporterObjRef->OnProgress_Internal(60);
|
||||
|
||||
drwav_uninit(&wavEncode);
|
||||
return true;
|
||||
}
|
||||
|
||||
TEnumAsByte<AudioFormat> URuntimeAudioImporterLibrary::GetAudioFormat(const FString& filePath)
|
||||
{
|
||||
if (FPaths::GetExtension(filePath, false).Equals(TEXT("mp3"), ESearchCase::IgnoreCase)) {
|
||||
return AudioFormat::MP3;
|
||||
}
|
||||
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)) {
|
||||
return AudioFormat::FLAC;
|
||||
}
|
||||
else {
|
||||
return AudioFormat::INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
void URuntimeAudioImporterLibrary::OnProgress_Internal(int32 Percentage)
|
||||
{
|
||||
AsyncTask(ENamedThreads::GameThread, [=]() {
|
||||
OnProgress.Broadcast(Percentage);
|
||||
});
|
||||
}
|
||||
|
||||
void URuntimeAudioImporterLibrary::OnResult_Internal(USoundWave* ReadySoundWave, TEnumAsByte < TranscodingStatus > Status)
|
||||
{
|
||||
AsyncTask(ENamedThreads::GameThread, [=]() {
|
||||
RemoveFromRoot();
|
||||
OnResult.Broadcast(ReadySoundWave, Status);
|
||||
});
|
||||
}
|
||||
-79
@@ -1,79 +0,0 @@
|
||||
// Respirant 2020.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Kismet/BlueprintFunctionLibrary.h"
|
||||
|
||||
#include "Sound/SoundWave.h"
|
||||
|
||||
#include "Developer/TargetPlatform/Public/Interfaces/IAudioFormat.h"
|
||||
|
||||
#include "RuntimeAudioImporterBPLibrary.generated.h"
|
||||
|
||||
|
||||
UENUM(BlueprintType)
|
||||
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")
|
||||
};
|
||||
|
||||
UENUM(BlueprintType)
|
||||
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)
|
||||
};
|
||||
|
||||
UCLASS()
|
||||
class RUNTIMEAUDIOIMPORTER_API URuntimeAudioImporterBPLibrary : public UBlueprintFunctionLibrary
|
||||
{
|
||||
GENERATED_UCLASS_BODY()
|
||||
public:
|
||||
|
||||
/**
|
||||
* Transcode audio file to USoundWave static object
|
||||
*
|
||||
* @param filePath Path to the audio file to import
|
||||
* @param Format Audio file format (extension)
|
||||
* @param status Final import status (TranscodingStatus)
|
||||
* @param DefineFormatAutomatically Whether to define format (extension) automatically or not
|
||||
* @return Returns USoundWave Static Object.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, meta = (Keywords = "Importer, Transcoder, Converter, MP3, FLAC, WAV", ToolTip = "Transcode audio file to USoundWave static object"), Category = "RuntimeAudioImporter")
|
||||
static class USoundWave* GetSoundWaveFromAudioFile(const FString & filePath, TEnumAsByte < AudioFormat > Format, TEnumAsByte < TranscodingStatus > & status, bool DefineFormatAutomatically);
|
||||
|
||||
/**
|
||||
* Destroy USoundWave static object
|
||||
*
|
||||
* @return Returns true - success, false - failure.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, meta = (Keywords = "Importer, Transcoder, Converter, MP3, FLAC, WAV, Destroy", ToolTip = "After the Soundwave is no longer needed, you need to call this function to free memory"), Category = "RuntimeAudioImporter")
|
||||
static bool DestroySoundWave(USoundWave * ReadySoundWave);
|
||||
|
||||
private:
|
||||
|
||||
/* Main internal get USoundWave from audio file */
|
||||
static class USoundWave* GetUSoundWaveFromAudioFile_Internal(const FString & filePath, TEnumAsByte < AudioFormat > Format, TEnumAsByte < TranscodingStatus > & status, bool DefineFormatAutomatically);
|
||||
|
||||
/* Get USoundWave static object reference */
|
||||
static class USoundWave* GetSoundWaveObject(const uint8 * WaveData, int32 WaveDataSize, TEnumAsByte < TranscodingStatus > & status);
|
||||
|
||||
/* Transcode Audio to Wave Data */
|
||||
static bool TranscodeAudioToWaveData(const char* filePath, TEnumAsByte < AudioFormat > Format, TEnumAsByte < TranscodingStatus > & status, uint64 & framesToWrite, int16_t * &pSampleData, uint32 & channels, uint32 & sampleRate);
|
||||
|
||||
/* Automatically get audio format. Internal use only recommended */
|
||||
static TEnumAsByte<AudioFormat> GetAudioFormat(const FString & filePath);
|
||||
};
|
||||
+175
@@ -0,0 +1,175 @@
|
||||
// Respirant 2020.
|
||||
#pragma once
|
||||
|
||||
#include "Sound/SoundWave.h"
|
||||
//#include "Developer/TargetPlatform/Public/Interfaces/IAudioFormat.h"
|
||||
#include "Async/Async.h"
|
||||
#include "RuntimeAudioImporterLibrary.generated.h"
|
||||
|
||||
/**
|
||||
* Possible audio importing results
|
||||
*/
|
||||
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")
|
||||
};
|
||||
|
||||
/**
|
||||
* Possible audio formats
|
||||
*/
|
||||
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)
|
||||
};
|
||||
|
||||
/**
|
||||
* Declare delegate which will be called during the transcoding process
|
||||
*
|
||||
* @param Percentage Percentage of download completed (0-100%)
|
||||
* @note The impementation of dispatcher is not fully right. The data is taken approximately, and will not show exactly the correct percentage of transcoding progress
|
||||
*/
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnProgress, const int32, Percentage);
|
||||
|
||||
/**
|
||||
* Declare a delegate that will be called on the transcoding result
|
||||
*
|
||||
* @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);
|
||||
|
||||
UCLASS(BlueprintType, Category = "RuntimeFilesDownloader")
|
||||
class RUNTIMEAUDIOIMPORTER_API URuntimeAudioImporterLibrary : public UObject
|
||||
{
|
||||
GENERATED_UCLASS_BODY()
|
||||
public:
|
||||
/**
|
||||
* Bind to know when the transcoding is on progress.
|
||||
*/
|
||||
UPROPERTY(BlueprintAssignable, Category = "RuntimeAudioImporter")
|
||||
FOnProgress OnProgress;
|
||||
|
||||
/**
|
||||
* Bind to know when the transcoding is complete (even if it fails).
|
||||
*/
|
||||
UPROPERTY(BlueprintAssignable, Category = "RuntimeAudioImporter")
|
||||
FOnResult OnResult;
|
||||
|
||||
URuntimeAudioImporterLibrary();
|
||||
~URuntimeAudioImporterLibrary();
|
||||
|
||||
/**
|
||||
* 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();
|
||||
|
||||
/**
|
||||
* Transcode audio file to USoundWave static object
|
||||
*
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* Destroy USoundWave static object. After the Soundwave is no longer needed, you need to call this function to free memory
|
||||
*
|
||||
* @param ReadySoundWave SoundWave Object need to be destroyed
|
||||
* @return Whether the descruction of USoundWave was successfull or not
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, meta = (Keywords = "MP3, FLAC, WAV, Destroy"), Category = "RuntimeAudioImporter")
|
||||
static bool DestroySoundWave(USoundWave * ReadySoundWave);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Internal main audio transcoding function
|
||||
*
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* Get USoundWave static object reference from 16-bit WAV data
|
||||
*
|
||||
* @param RuntimeAudioImporterObjRef Reference to the created RuntimeAudioImporter object
|
||||
* @param WaveData Reference to memory location of 16-bit WAV byte data
|
||||
* @param WaveDataSize Memory size allocated for 16-bit WAV byte data
|
||||
* @param Status Reference to TranscodingStatus Enumerator in case an error occurs
|
||||
* @return Returns a ready USoundWave object
|
||||
*/
|
||||
static class USoundWave* GetSoundWaveObject(URuntimeAudioImporterLibrary * RuntimeAudioImporterObjRef, const uint8 * WaveData, int32 WaveDataSize, TEnumAsByte < TranscodingStatus > & Status);
|
||||
|
||||
/**
|
||||
* Transcode Audio from File to PCM Data
|
||||
*
|
||||
* @param RuntimeAudioImporterObjRef Reference to the created RuntimeAudioImporter object
|
||||
* @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
|
||||
* @param pSampleData Ready PCM sample data for which memory has been allocated
|
||||
* @param channels Quantity of channels (e.g. 1, 2, etc)
|
||||
* @param sampleRate Sample rate of the audio
|
||||
* @return Whether the transcoding was successful or not
|
||||
*/
|
||||
static bool TranscodeAudioFileToPCMData(URuntimeAudioImporterLibrary * RuntimeAudioImporterObjRef, const char* 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
|
||||
*
|
||||
* @param RuntimeAudioImporterObjRef Reference to the created RuntimeAudioImporter object
|
||||
* @param WaveData Reference to memory location of 16-bit WAV byte data
|
||||
* @param WaveDataSize Memory size allocated for 16-bit WAV byte data
|
||||
* @param framesToWrite PCM frames which will be written for WAV data
|
||||
* @param pSampleData Ready WAV sample data for which memory has been allocated
|
||||
* @param channels Quantity of channels (e.g. 1, 2, etc)
|
||||
* @param sampleRate Sample rate of the audio
|
||||
* @return Whether the transcoding was successful or not
|
||||
*/
|
||||
static bool TranscodePCMToWAVData(URuntimeAudioImporterLibrary* RuntimeAudioImporterObjRef, 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)
|
||||
* @return Returns the found audio format (e.g. mp3. flac, etc) by AudioFormat Enumerator
|
||||
*/
|
||||
static TEnumAsByte<AudioFormat> GetAudioFormat(const FString & filePath);
|
||||
|
||||
/**
|
||||
* Audio transcoding progress callback
|
||||
* @param Percentage Percentage of download completed (0-100%)
|
||||
* @note The impementation of dispatcher is not fully right. The data is taken approximately, and will not show exactly the correct percentage of transcoding progress
|
||||
*/
|
||||
void OnProgress_Internal(int32 Percentage);
|
||||
|
||||
/**
|
||||
* Audio transcoding finished callback
|
||||
* @param ReadySoundWave A ready USoundWave object
|
||||
* @param Status Reference to TranscodingStatus Enumerator
|
||||
*/
|
||||
void OnResult_Internal(USoundWave * ReadySoundWave, TEnumAsByte < TranscodingStatus > Status);
|
||||
};
|
||||
@@ -1,15 +1,13 @@
|
||||
// Respirant 2020.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using UnrealBuildTool;
|
||||
|
||||
public class RuntimeAudioImporter : ModuleRules
|
||||
{
|
||||
public RuntimeAudioImporter(ReadOnlyTargetRules Target) : base(Target)
|
||||
{
|
||||
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
|
||||
|
||||
public RuntimeAudioImporter(ReadOnlyTargetRules Target) : base(Target)
|
||||
{
|
||||
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
|
||||
|
||||
PublicDefinitions.AddRange(
|
||||
new string[] {
|
||||
@@ -19,35 +17,33 @@ public class RuntimeAudioImporter : ModuleRules
|
||||
}
|
||||
);
|
||||
|
||||
//PrivateIncludePaths.Add(Path.Combine(ModuleDirectory, "ThirdParty"));
|
||||
|
||||
|
||||
PublicDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"Core",
|
||||
new string[]
|
||||
{
|
||||
"Core",
|
||||
// ... add other public dependencies that you statically link with here ...
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
PrivateDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"CoreUObject",
|
||||
"Engine",
|
||||
"Slate",
|
||||
"SlateCore",
|
||||
);
|
||||
|
||||
|
||||
PrivateDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"CoreUObject",
|
||||
"Engine",
|
||||
"Slate",
|
||||
"SlateCore",
|
||||
// ... add private dependencies that you statically link with here ...
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
DynamicallyLoadedModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
);
|
||||
|
||||
|
||||
DynamicallyLoadedModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
// ... add any modules that your module loads dynamically here ...
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user