mirror of
https://github.com/storytold/RuntimeAudioImporter.git
synced 2026-10-25 08:39:53 +00:00
Encoding changes, UNIX/Mac optimising
This commit is contained in:
+126
-58
@@ -5,7 +5,6 @@
|
|||||||
URuntimeAudioImporterLibrary::URuntimeAudioImporterLibrary(const FObjectInitializer& ObjectInitializer)
|
URuntimeAudioImporterLibrary::URuntimeAudioImporterLibrary(const FObjectInitializer& ObjectInitializer)
|
||||||
: Super(ObjectInitializer)
|
: Super(ObjectInitializer)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
URuntimeAudioImporterLibrary* URuntimeAudioImporterLibrary::CreateRuntimeAudioImporter()
|
URuntimeAudioImporterLibrary* URuntimeAudioImporterLibrary::CreateRuntimeAudioImporter()
|
||||||
@@ -15,18 +14,20 @@ URuntimeAudioImporterLibrary* URuntimeAudioImporterLibrary::CreateRuntimeAudioIm
|
|||||||
return Importer;
|
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, [=]() {
|
AsyncTask(ENamedThreads::AnyThread, [=]()
|
||||||
GetUSoundWaveFromAudioFile_Internal(filePath, Format, DefineFormatAutomatically);
|
{
|
||||||
|
GetUSoundWaveFromAudioFile_Internal(FilePath, Format, DefineFormatAutomatically);
|
||||||
});
|
});
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool URuntimeAudioImporterLibrary::DestroySoundWave(USoundWave* ReadySoundWave) {
|
bool URuntimeAudioImporterLibrary::DestroySoundWave(USoundWave* ReadySoundWave)
|
||||||
|
{
|
||||||
if (ReadySoundWave->IsValidLowLevel()) {
|
if (ReadySoundWave->IsValidLowLevel())
|
||||||
|
{
|
||||||
//Begin USoundWave destroy (block changing some data)
|
//Begin USoundWave destroy (block changing some data)
|
||||||
ReadySoundWave->ConditionalBeginDestroy();
|
ReadySoundWave->ConditionalBeginDestroy();
|
||||||
ReadySoundWave->BeginDestroy();
|
ReadySoundWave->BeginDestroy();
|
||||||
@@ -34,7 +35,8 @@ bool URuntimeAudioImporterLibrary::DestroySoundWave(USoundWave* ReadySoundWave)
|
|||||||
//Delete all audio data from physical memory
|
//Delete all audio data from physical memory
|
||||||
ReadySoundWave->RawData.RemoveBulkData();
|
ReadySoundWave->RawData.RemoveBulkData();
|
||||||
ReadySoundWave->RemoveAudioResource();
|
ReadySoundWave->RemoveAudioResource();
|
||||||
AsyncTask(ENamedThreads::AudioThread, [=]() {
|
AsyncTask(ENamedThreads::AudioThread, [=]()
|
||||||
|
{
|
||||||
ReadySoundWave->InvalidateCompressedData(true);
|
ReadySoundWave->InvalidateCompressedData(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -44,7 +46,8 @@ bool URuntimeAudioImporterLibrary::DestroySoundWave(USoundWave* ReadySoundWave)
|
|||||||
ReadySoundWave->FinishDestroy();
|
ReadySoundWave->FinishDestroy();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -63,10 +66,12 @@ void* unreal_malloc(size_t sz, void* pUserData)
|
|||||||
{
|
{
|
||||||
return FMemory::Malloc(sz);
|
return FMemory::Malloc(sz);
|
||||||
}
|
}
|
||||||
|
|
||||||
void* unreal_realloc(void* p, size_t sz, void* pUserData)
|
void* unreal_realloc(void* p, size_t sz, void* pUserData)
|
||||||
{
|
{
|
||||||
return FMemory::Realloc(p, sz);
|
return FMemory::Realloc(p, sz);
|
||||||
}
|
}
|
||||||
|
|
||||||
void unreal_free(void* p, void* pUserData)
|
void unreal_free(void* p, void* pUserData)
|
||||||
{
|
{
|
||||||
FMemory::Free(p);
|
FMemory::Free(p);
|
||||||
@@ -77,9 +82,6 @@ bool URuntimeAudioImporterLibrary::ExportSoundWaveToFile(USoundWave* SoundWaveTo
|
|||||||
uint8* PCMData = SoundWaveToExport->RawPCMData;
|
uint8* PCMData = SoundWaveToExport->RawPCMData;
|
||||||
size_t PCMDataSize = SoundWaveToExport->RawPCMDataSize;
|
size_t PCMDataSize = SoundWaveToExport->RawPCMDataSize;
|
||||||
|
|
||||||
uint32 SampleRate;
|
|
||||||
uint16 ChannelCount;
|
|
||||||
|
|
||||||
drwav_allocation_callbacks allocationCallbacksDecoding;
|
drwav_allocation_callbacks allocationCallbacksDecoding;
|
||||||
allocationCallbacksDecoding.pUserData = NULL;
|
allocationCallbacksDecoding.pUserData = NULL;
|
||||||
allocationCallbacksDecoding.onMalloc = unreal_malloc;
|
allocationCallbacksDecoding.onMalloc = unreal_malloc;
|
||||||
@@ -88,8 +90,9 @@ bool URuntimeAudioImporterLibrary::ExportSoundWaveToFile(USoundWave* SoundWaveTo
|
|||||||
|
|
||||||
drwav_uint64 framesToWrite = PCMDataSize / sizeof(DR_WAVE_FORMAT_PCM) / SoundWaveToExport->NumChannels;
|
drwav_uint64 framesToWrite = PCMDataSize / sizeof(DR_WAVE_FORMAT_PCM) / SoundWaveToExport->NumChannels;
|
||||||
|
|
||||||
ChannelCount = SoundWaveToExport->NumChannels;
|
uint16 ChannelCount = SoundWaveToExport->NumChannels;
|
||||||
SampleRate = FGenericPlatformMath::RoundToInt((framesToWrite / (float)SoundWaveToExport->GetDuration() * ChannelCount));
|
uint32 SampleRate = FGenericPlatformMath::RoundToInt(
|
||||||
|
(framesToWrite / (float)SoundWaveToExport->GetDuration() * ChannelCount));
|
||||||
|
|
||||||
drwav wavEncode;
|
drwav wavEncode;
|
||||||
|
|
||||||
@@ -100,7 +103,14 @@ bool URuntimeAudioImporterLibrary::ExportSoundWaveToFile(USoundWave* SoundWaveTo
|
|||||||
format.sampleRate = SampleRate;
|
format.sampleRate = SampleRate;
|
||||||
format.bitsPerSample = 16;
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,48 +120,61 @@ bool URuntimeAudioImporterLibrary::ExportSoundWaveToFile(USoundWave* SoundWaveTo
|
|||||||
return true;
|
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;
|
TEnumAsByte<TranscodingStatus> Status;
|
||||||
if (FPaths::FileExists(filePath) != true) {
|
if (FPaths::FileExists(FilePath) != true)
|
||||||
|
{
|
||||||
Status = TranscodingStatus::AudioDoesNotExist;
|
Status = TranscodingStatus::AudioDoesNotExist;
|
||||||
|
|
||||||
// Callback Dispatcher OnResult
|
// Callback Dispatcher OnResult
|
||||||
OnResult_Internal(nullptr, Status);
|
OnResult_Internal(nullptr, Status);
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
int16_t* pSampleData;
|
int16_t* pSampleData;
|
||||||
uint64 framesToWrite;
|
uint64 framesToWrite;
|
||||||
uint32 channels;
|
uint32 channels;
|
||||||
uint32 sampleRate;
|
uint32 sampleRate;
|
||||||
|
|
||||||
if (DefineFormatAutomatically) {
|
if (DefineFormatAutomatically)
|
||||||
Format = GetAudioFormat(filePath);
|
{
|
||||||
|
Format = GetAudioFormat(FilePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Format == AudioFormat::INVALID) {
|
if (Format == AudioFormat::INVALID)
|
||||||
|
{
|
||||||
Status = TranscodingStatus::InvalidAudioFormat;
|
Status = TranscodingStatus::InvalidAudioFormat;
|
||||||
|
|
||||||
// Callback Dispatcher OnResult
|
// Callback Dispatcher OnResult
|
||||||
OnResult_Internal(nullptr, Status);
|
OnResult_Internal(nullptr, Status);
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
if (TranscodeAudioFileToPCMData((wchar_t*)filePath.GetCharArray().GetData(), Format, Status, framesToWrite, pSampleData, channels, sampleRate) == false) {
|
{
|
||||||
|
if (TranscodeAudioFileToPCMData(FilePath, Format, Status, framesToWrite, pSampleData, channels, sampleRate)
|
||||||
|
== false)
|
||||||
|
{
|
||||||
// Callback Dispatcher OnResult
|
// Callback Dispatcher OnResult
|
||||||
OnResult_Internal(nullptr, Status);
|
OnResult_Internal(nullptr, Status);
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
void* WaveData;
|
void* WaveData;
|
||||||
size_t WaveDataSize;
|
size_t WaveDataSize;
|
||||||
|
|
||||||
if (TranscodePCMToWAVData(*&WaveData, *&WaveDataSize, framesToWrite, *&pSampleData, channels, sampleRate) == false) {
|
if (TranscodePCMToWAVData(*&WaveData, *&WaveDataSize, framesToWrite, *&pSampleData, channels,
|
||||||
|
sampleRate) == false)
|
||||||
|
{
|
||||||
FMemory::Free(pSampleData);
|
FMemory::Free(pSampleData);
|
||||||
|
|
||||||
// Callback Dispatcher OnResult
|
// Callback Dispatcher OnResult
|
||||||
OnResult_Internal(nullptr, Status);
|
OnResult_Internal(nullptr, Status);
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
USoundWave* ReadyUSoundWave = GetSoundWaveObject((const uint8*)WaveData, WaveDataSize, Status); // A ready USoundWave static object
|
{
|
||||||
|
USoundWave* ReadyUSoundWave = GetSoundWaveObject((const uint8*)WaveData, WaveDataSize, Status);
|
||||||
|
// A ready USoundWave static object
|
||||||
|
|
||||||
/* Free temporary transcoding data */
|
/* Free temporary transcoding data */
|
||||||
FMemory::Free(WaveData);
|
FMemory::Free(WaveData);
|
||||||
@@ -164,18 +187,18 @@ URuntimeAudioImporterLibrary* URuntimeAudioImporterLibrary::GetUSoundWaveFromAud
|
|||||||
// Callback Dispatcher OnResult
|
// Callback Dispatcher OnResult
|
||||||
OnResult_Internal(ReadyUSoundWave, Status);
|
OnResult_Internal(ReadyUSoundWave, Status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return this;
|
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());
|
USoundWave* sw = NewObject<USoundWave>(USoundWave::StaticClass());
|
||||||
if (!sw) {
|
if (!sw)
|
||||||
|
{
|
||||||
Status = TranscodingStatus::USoundWaveDeclarationError;
|
Status = TranscodingStatus::USoundWaveDeclarationError;
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
@@ -190,7 +213,8 @@ class USoundWave* URuntimeAudioImporterLibrary::GetSoundWaveObject(const uint8*
|
|||||||
// Callback Dispatcher OnProgress (not completely accurate implementation)
|
// Callback Dispatcher OnProgress (not completely accurate implementation)
|
||||||
OnProgress_Internal(75);
|
OnProgress_Internal(75);
|
||||||
|
|
||||||
if (*WaveInfo.pBitsPerSample != 16) {
|
if (*WaveInfo.pBitsPerSample != 16)
|
||||||
|
{
|
||||||
Status = TranscodingStatus::UnsupportedBitDepth;
|
Status = TranscodingStatus::UnsupportedBitDepth;
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
@@ -222,17 +246,25 @@ class USoundWave* URuntimeAudioImporterLibrary::GetSoundWaveObject(const uint8*
|
|||||||
// Callback Dispatcher OnProgress (not completely accurate implementation)
|
// Callback Dispatcher OnProgress (not completely accurate implementation)
|
||||||
OnProgress_Internal(85);
|
OnProgress_Internal(85);
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
if (ErrorReason.Equals(TEXT("Invalid WAVE file."), ESearchCase::CaseSensitive)) {
|
{
|
||||||
|
if (ErrorReason.Equals(TEXT("Invalid WAVE file."), ESearchCase::CaseSensitive))
|
||||||
|
{
|
||||||
Status = TranscodingStatus::InvalidWAVEData;
|
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;
|
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;
|
Status = TranscodingStatus::InvalidSubformat;
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
Status = TranscodingStatus::UnrecognizedReadWaveError;
|
Status = TranscodingStatus::UnrecognizedReadWaveError;
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@@ -248,7 +280,8 @@ class USoundWave* URuntimeAudioImporterLibrary::GetSoundWaveObject(const uint8*
|
|||||||
// Callback Dispatcher OnProgress (not completely accurate implementation)
|
// Callback Dispatcher OnProgress (not completely accurate implementation)
|
||||||
OnProgress_Internal(95);
|
OnProgress_Internal(95);
|
||||||
|
|
||||||
if (!sw) {
|
if (!sw)
|
||||||
|
{
|
||||||
Status = TranscodingStatus::InvalidUSoundWave;
|
Status = TranscodingStatus::InvalidUSoundWave;
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
@@ -256,7 +289,10 @@ class USoundWave* URuntimeAudioImporterLibrary::GetSoundWaveObject(const uint8*
|
|||||||
return sw;
|
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);
|
OnProgress_Internal(5);
|
||||||
switch (Format)
|
switch (Format)
|
||||||
@@ -270,15 +306,22 @@ bool URuntimeAudioImporterLibrary::TranscodeAudioFileToPCMData(const wchar_t* fi
|
|||||||
allocationCallbacksDecoding.onFree = unreal_free;
|
allocationCallbacksDecoding.onFree = unreal_free;
|
||||||
|
|
||||||
drmp3 mp3;
|
drmp3 mp3;
|
||||||
if (!drmp3_init_file_w(&mp3, filePath, &allocationCallbacksDecoding)) {
|
#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;
|
Status = TranscodingStatus::FailedToOpenStream;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
// Callback Dispatcher OnProgress (not completely accurate implementation)
|
// Callback Dispatcher OnProgress (not completely accurate implementation)
|
||||||
OnProgress_Internal(10);
|
OnProgress_Internal(10);
|
||||||
|
|
||||||
pSampleData = (int16_t*)FMemory::Malloc((size_t)drmp3_get_pcm_frame_count(&mp3) * mp3.channels * sizeof(int16_t));
|
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)
|
// Callback Dispatcher OnProgress (not completely accurate implementation)
|
||||||
OnProgress_Internal(15);
|
OnProgress_Internal(15);
|
||||||
@@ -304,15 +347,22 @@ bool URuntimeAudioImporterLibrary::TranscodeAudioFileToPCMData(const wchar_t* fi
|
|||||||
allocationCallbacksDecoding.onFree = unreal_free;
|
allocationCallbacksDecoding.onFree = unreal_free;
|
||||||
|
|
||||||
drwav wav;
|
drwav wav;
|
||||||
if (!drwav_init_file_w(&wav, filePath, &allocationCallbacksDecoding)) {
|
#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;
|
Status = TranscodingStatus::FailedToOpenStream;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
// Callback Dispatcher OnProgress (not completely accurate implementation)
|
// Callback Dispatcher OnProgress (not completely accurate implementation)
|
||||||
OnProgress_Internal(10);
|
OnProgress_Internal(10);
|
||||||
|
|
||||||
pSampleData = (int16_t*)FMemory::Malloc((size_t)wav.totalPCMFrameCount * wav.channels * sizeof(int16_t));
|
pSampleData = (int16_t*)
|
||||||
|
FMemory::Malloc((size_t)wav.totalPCMFrameCount * wav.channels * sizeof(int16_t));
|
||||||
|
|
||||||
// Callback Dispatcher OnProgress (not completely accurate implementation)
|
// Callback Dispatcher OnProgress (not completely accurate implementation)
|
||||||
OnProgress_Internal(15);
|
OnProgress_Internal(15);
|
||||||
@@ -337,16 +387,25 @@ bool URuntimeAudioImporterLibrary::TranscodeAudioFileToPCMData(const wchar_t* fi
|
|||||||
allocationCallbacksDecoding.onRealloc = unreal_realloc;
|
allocationCallbacksDecoding.onRealloc = unreal_realloc;
|
||||||
allocationCallbacksDecoding.onFree = unreal_free;
|
allocationCallbacksDecoding.onFree = unreal_free;
|
||||||
|
|
||||||
drflac* pFlac = drflac_open_file_w(filePath, &allocationCallbacksDecoding);
|
#if PLATFORM_WINDOWS
|
||||||
if (pFlac == NULL) {
|
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;
|
Status = TranscodingStatus::FailedToOpenStream;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
// Callback Dispatcher OnProgress (not completely accurate implementation)
|
// Callback Dispatcher OnProgress (not completely accurate implementation)
|
||||||
OnProgress_Internal(10);
|
OnProgress_Internal(10);
|
||||||
|
|
||||||
pSampleData = (int16_t*)FMemory::Malloc((size_t)pFlac->totalPCMFrameCount * pFlac->channels * sizeof(int16_t));
|
pSampleData = (int16_t*)FMemory::Malloc(
|
||||||
|
(size_t)pFlac->totalPCMFrameCount * pFlac->channels * sizeof(int16_t));
|
||||||
|
|
||||||
// Callback Dispatcher OnProgress (not completely accurate implementation)
|
// Callback Dispatcher OnProgress (not completely accurate implementation)
|
||||||
OnProgress_Internal(15);
|
OnProgress_Internal(15);
|
||||||
@@ -372,7 +431,9 @@ bool URuntimeAudioImporterLibrary::TranscodeAudioFileToPCMData(const wchar_t* fi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 wavEncode;
|
||||||
drwav_data_format format;
|
drwav_data_format format;
|
||||||
format.container = drwav_container_riff;
|
format.container = drwav_container_riff;
|
||||||
@@ -386,7 +447,8 @@ bool URuntimeAudioImporterLibrary::TranscodePCMToWAVData(void*& WaveData, size_t
|
|||||||
allocationCallbacks.onMalloc = unreal_malloc;
|
allocationCallbacks.onMalloc = unreal_malloc;
|
||||||
allocationCallbacks.onRealloc = unreal_realloc;
|
allocationCallbacks.onRealloc = unreal_realloc;
|
||||||
allocationCallbacks.onFree = unreal_free;
|
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
|
return false; //error
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -404,30 +466,36 @@ bool URuntimeAudioImporterLibrary::TranscodePCMToWAVData(void*& WaveData, size_t
|
|||||||
|
|
||||||
TEnumAsByte<AudioFormat> URuntimeAudioImporterLibrary::GetAudioFormat(const FString& filePath)
|
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;
|
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;
|
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;
|
return AudioFormat::FLAC;
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
return AudioFormat::INVALID;
|
return AudioFormat::INVALID;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void URuntimeAudioImporterLibrary::OnProgress_Internal(int32 Percentage)
|
void URuntimeAudioImporterLibrary::OnProgress_Internal(int32 Percentage)
|
||||||
{
|
{
|
||||||
AsyncTask(ENamedThreads::GameThread, [=]() {
|
AsyncTask(ENamedThreads::GameThread, [=]()
|
||||||
|
{
|
||||||
OnProgress.Broadcast(Percentage);
|
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();
|
RemoveFromRoot();
|
||||||
OnResult.Broadcast(ReadySoundWave, Status);
|
OnResult.Broadcast(ReadySoundWave, Status);
|
||||||
});
|
});
|
||||||
|
|||||||
+25
-14
@@ -51,7 +51,8 @@ DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnProgress, const int32, Percentage
|
|||||||
* @param ReadySoundWave Ready USoundWave Object
|
* @param ReadySoundWave Ready USoundWave Object
|
||||||
* @param Status Reference to TranscodingStatus Enumerator in case an error occurs
|
* @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")
|
UCLASS(BlueprintType, Category = "RuntimeFilesDownloader")
|
||||||
class RUNTIMEAUDIOIMPORTER_API URuntimeAudioImporterLibrary : public UObject
|
class RUNTIMEAUDIOIMPORTER_API URuntimeAudioImporterLibrary : public UObject
|
||||||
@@ -75,19 +76,22 @@ public:
|
|||||||
*
|
*
|
||||||
* @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.
|
* @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")
|
UFUNCTION(BlueprintCallable, meta = (Keywords = "Create, Audio, Runtime, MP3, FLAC, WAV"), Category =
|
||||||
|
"RuntimeAudioImporter")
|
||||||
static URuntimeAudioImporterLibrary* CreateRuntimeAudioImporter();
|
static URuntimeAudioImporterLibrary* CreateRuntimeAudioImporter();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transcode audio file to USoundWave static object
|
* 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 Format Audio file format (extension)
|
||||||
* @param DefineFormatAutomatically Whether to define format (extension) automatically or not
|
* @param DefineFormatAutomatically Whether to define format (extension) automatically or not
|
||||||
* @return Returns itself.
|
* @return Returns itself.
|
||||||
*/
|
*/
|
||||||
UFUNCTION(BlueprintCallable, meta = (Keywords = "Importer, Transcoder, Converter, Runtime, MP3, FLAC, WAV"), Category = "RuntimeAudioImporter")
|
UFUNCTION(BlueprintCallable, meta = (Keywords = "Importer, Transcoder, Converter, Runtime, MP3, FLAC, WAV"),
|
||||||
URuntimeAudioImporterLibrary * ImportAudioFromFile(const FString & filePath, TEnumAsByte < AudioFormat > Format, bool DefineFormatAutomatically);
|
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
|
* Destroy USoundWave static object. After the Soundwave is no longer needed, you need to call this function to free memory
|
||||||
@@ -106,19 +110,22 @@ public:
|
|||||||
* @return Whether the save was successfull or not
|
* @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.
|
* @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")
|
UFUNCTION(BlueprintCallable, meta = (Keywords = "Exporter, Export, Save, Runtime, WAV"), Category =
|
||||||
|
"RuntimeAudioImporter")
|
||||||
static bool ExportSoundWaveToFile(USoundWave* SoundWaveToExport, FString PathToExport);
|
static bool ExportSoundWaveToFile(USoundWave* SoundWaveToExport, FString PathToExport);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/**
|
/**
|
||||||
* Internal main audio transcoding function
|
* 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 Format Audio file format (extension)
|
||||||
* @param DefineFormatAutomatically Whether to define format (extension) automatically or not
|
* @param DefineFormatAutomatically Whether to define format (extension) automatically or not
|
||||||
* @return Returns itself
|
* @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
|
* 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
|
* @param Status Reference to TranscodingStatus Enumerator in case an error occurs
|
||||||
* @return Returns a ready USoundWave object
|
* @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
|
* 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 Format Format of the audio file (e.g. mp3. flac, etc)
|
||||||
* @param Status Reference to TranscodingStatus Enumerator in case an error occurs
|
* @param Status Reference to TranscodingStatus Enumerator in case an error occurs
|
||||||
* @param framesToWrite PCM frames ready to be written
|
* @param framesToWrite PCM frames ready to be written
|
||||||
@@ -142,7 +150,9 @@ private:
|
|||||||
* @param sampleRate Sample rate of the audio
|
* @param sampleRate Sample rate of the audio
|
||||||
* @return Whether the transcoding was successful or not
|
* @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
|
* Transcode Audio from PCM to 16-bit WAV data
|
||||||
@@ -155,13 +165,14 @@ private:
|
|||||||
* @param sampleRate Sample rate of the audio
|
* @param sampleRate Sample rate of the audio
|
||||||
* @return Whether the transcoding was successful or not
|
* @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
|
* @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
|
* Audio transcoding progress callback
|
||||||
|
|||||||
Reference in New Issue
Block a user