add plugin data

This commit is contained in:
Respirant
2020-12-11 14:23:40 +01:00
commit 54559198f0
14 changed files with 23819 additions and 0 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

@@ -0,0 +1,24 @@
{
"FileVersion": 3,
"Version": 1,
"VersionName": "1.0",
"FriendlyName": "RuntimeAudioImporter",
"Description": "Runtime Audio Importer plugin for Unreal Engine 4. Supports mp3, wav and flac formats.",
"Category": "Audio",
"CreatedBy": "Respirant",
"CreatedByURL": "https://github.com/Respirant/RuntimeAudioImporter",
"DocsURL": "https://github.com/Respirant/RuntimeAudioImporter",
"MarketplaceURL": "",
"SupportURL": "https://telegram.me/Respirant",
"CanContainContent": false,
"IsBetaVersion": false,
"IsExperimentalVersion": false,
"Installed": true,
"Modules": [
{
"Name": "RuntimeAudioImporter",
"Type": "Runtime",
"LoadingPhase": "Default"
}
]
}
@@ -0,0 +1,22 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#include "RuntimeAudioImporter.h"
#define LOCTEXT_NAMESPACE "FRuntimeAudioImporterModule"
void FRuntimeAudioImporterModule::StartupModule()
{
// This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module
}
void FRuntimeAudioImporterModule::ShutdownModule()
{
// This function may be called during shutdown to clean up your module. For modules that support dynamic reloading,
// we call this function before unloading the module.
}
#undef LOCTEXT_NAMESPACE
IMPLEMENT_MODULE(FRuntimeAudioImporterModule, RuntimeAudioImporter)
@@ -0,0 +1,218 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#include "RuntimeAudioImporterBPLibrary.h"
#include "RuntimeAudioImporter.h"
URuntimeAudioImporterBPLibrary::URuntimeAudioImporterBPLibrary(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
}
/* Transcode audio file to 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);
}
/* 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::Success;
return sw;
}
#define DR_WAV_IMPLEMENTATION
#include "dr_libs/dr_wav.h"
#define DR_MP3_IMPLEMENTATION
#include "dr_libs/dr_mp3.h"
#define DR_FLAC_IMPLEMENTATION
#include "dr_libs/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(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 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 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 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;
}
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,16 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "Modules/ModuleManager.h"
class FRuntimeAudioImporterModule : public IModuleInterface
{
public:
/** IModuleInterface implementation */
virtual void StartupModule() override;
virtual void ShutdownModule() override;
};
@@ -0,0 +1,69 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "Kismet/BlueprintFunctionLibrary.h"
#include "Runtime/Engine/Classes/Sound/SoundWave.h"
//#include "Runtime/Engine/Public/VorbisAudioInfo.h"
#include "Developer/TargetPlatform/Public/Interfaces/IAudioFormat.h"
#include "Async/Async.h"
#include "RuntimeAudioImporterBPLibrary.generated.h"
UENUM(BlueprintType)
enum TranscodingStatus
{
Success 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 */
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);
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);
};
@@ -0,0 +1,53 @@
// Some copyright should be here...
using UnrealBuildTool;
public class RuntimeAudioImporter : ModuleRules
{
public RuntimeAudioImporter(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
PublicIncludePaths.AddRange(
new string[] {
// ... add public include paths required here ...
}
);
PrivateIncludePaths.AddRange(
new string[] {
// ... add other private include paths required here ...
}
);
PublicDependencyModuleNames.AddRange(
new string[]
{
"Core",
// ... add other public dependencies that you statically link with here ...
}
);
PrivateDependencyModuleNames.AddRange(
new string[]
{
"CoreUObject",
"Engine",
"Slate",
"SlateCore",
// ... add private dependencies that you statically link with here ...
}
);
DynamicallyLoadedModuleNames.AddRange(
new string[]
{
// ... add any modules that your module loads dynamically here ...
}
);
}
}