2019-12-26 14:45:42 -05:00
|
|
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
2016-01-07 04:39:47 -05:00
|
|
|
|
Copying //UE4/Dev-Build to //UE4/Dev-Main (Source: //UE4/Dev-Build @ 3209340)
#lockdown Nick.Penwarden
#rb none
==========================
MAJOR FEATURES + CHANGES
==========================
Change 3209340 on 2016/11/23 by Ben.Marsh
Convert UE4 codebase to an "include what you use" model - where every header just includes the dependencies it needs, rather than every source file including large monolithic headers like Engine.h and UnrealEd.h.
Measured full rebuild times around 2x faster using XGE on Windows, and improvements of 25% or more for incremental builds and full rebuilds on most other platforms.
* Every header now includes everything it needs to compile.
* There's a CoreMinimal.h header that gets you a set of ubiquitous types from Core (eg. FString, FName, TArray, FVector, etc...). Most headers now include this first.
* There's a CoreTypes.h header that sets up primitive UE4 types and build macros (int32, PLATFORM_WIN64, etc...). All headers in Core include this first, as does CoreMinimal.h.
* Every .cpp file includes its matching .h file first.
* This helps validate that each header is including everything it needs to compile.
* No engine code includes a monolithic header such as Engine.h or UnrealEd.h any more.
* You will get a warning if you try to include one of these from the engine. They still exist for compatibility with game projects and do not produce warnings when included there.
* There have only been minor changes to our internal games down to accommodate these changes. The intent is for this to be as seamless as possible.
* No engine code explicitly includes a precompiled header any more.
* We still use PCHs, but they're force-included on the compiler command line by UnrealBuildTool instead. This lets us tune what they contain without breaking any existing include dependencies.
* PCHs are generated by a tool to get a statistical amount of coverage for the source files using it, and I've seeded the new shared PCHs to contain any header included by > 15% of source files.
Tool used to generate this transform is at Engine\Source\Programs\IncludeTool.
[CL 3209342 by Ben Marsh in Main branch]
2016-11-23 15:48:37 -05:00
|
|
|
#include "CoreMinimal.h"
|
|
|
|
|
#include "Misc/FileHelper.h"
|
|
|
|
|
#include "Misc/Paths.h"
|
|
|
|
|
#include "UnrealAudioTypes.h"
|
|
|
|
|
#include "UnrealAudioUtilities.h"
|
2015-07-14 13:35:50 -04:00
|
|
|
#include "UnrealAudioPrivate.h"
|
Copying //UE4/Dev-Build to //UE4/Dev-Main (Source: //UE4/Dev-Build @ 3209340)
#lockdown Nick.Penwarden
#rb none
==========================
MAJOR FEATURES + CHANGES
==========================
Change 3209340 on 2016/11/23 by Ben.Marsh
Convert UE4 codebase to an "include what you use" model - where every header just includes the dependencies it needs, rather than every source file including large monolithic headers like Engine.h and UnrealEd.h.
Measured full rebuild times around 2x faster using XGE on Windows, and improvements of 25% or more for incremental builds and full rebuilds on most other platforms.
* Every header now includes everything it needs to compile.
* There's a CoreMinimal.h header that gets you a set of ubiquitous types from Core (eg. FString, FName, TArray, FVector, etc...). Most headers now include this first.
* There's a CoreTypes.h header that sets up primitive UE4 types and build macros (int32, PLATFORM_WIN64, etc...). All headers in Core include this first, as does CoreMinimal.h.
* Every .cpp file includes its matching .h file first.
* This helps validate that each header is including everything it needs to compile.
* No engine code includes a monolithic header such as Engine.h or UnrealEd.h any more.
* You will get a warning if you try to include one of these from the engine. They still exist for compatibility with game projects and do not produce warnings when included there.
* There have only been minor changes to our internal games down to accommodate these changes. The intent is for this to be as seamless as possible.
* No engine code explicitly includes a precompiled header any more.
* We still use PCHs, but they're force-included on the compiler command line by UnrealBuildTool instead. This lets us tune what they contain without breaking any existing include dependencies.
* PCHs are generated by a tool to get a statistical amount of coverage for the source files using it, and I've seeded the new shared PCHs to contain any header included by > 15% of source files.
Tool used to generate this transform is at Engine\Source\Programs\IncludeTool.
[CL 3209342 by Ben Marsh in Main branch]
2016-11-23 15:48:37 -05:00
|
|
|
#include "Modules/ModuleManager.h"
|
2015-07-14 13:35:50 -04:00
|
|
|
|
|
|
|
|
#if ENABLE_UNREAL_AUDIO
|
|
|
|
|
|
|
|
|
|
namespace UAudio
|
|
|
|
|
{
|
|
|
|
|
// Virtual Sound File Function Pointers
|
|
|
|
|
typedef SoundFileCount(*VirtualSoundFileGetLengthFuncPtr)(void* UserData);
|
|
|
|
|
typedef SoundFileCount(*VirtualSoundFileSeekFuncPtr)(SoundFileCount Offset, int32 Mode, void* UserData);
|
|
|
|
|
typedef SoundFileCount(*VirtualSoundFileReadFuncPtr)(void* DataPtr, SoundFileCount ByteCount, void* UserData);
|
|
|
|
|
typedef SoundFileCount(*VirtualSoundFileWriteFuncPtr)(const void* DataPtr, SoundFileCount ByteCount, void* UserData);
|
|
|
|
|
typedef SoundFileCount(*VirtualSoundFileTellFuncPtr)(void* UserData);
|
|
|
|
|
|
|
|
|
|
// Struct describing function pointers to call for virtual file IO
|
|
|
|
|
struct FVirtualSoundFileCallbackInfo
|
|
|
|
|
{
|
|
|
|
|
VirtualSoundFileGetLengthFuncPtr VirtualSoundFileGetLength;
|
|
|
|
|
VirtualSoundFileSeekFuncPtr VirtualSoundFileSeek;
|
|
|
|
|
VirtualSoundFileReadFuncPtr VirtualSoundFileRead;
|
|
|
|
|
VirtualSoundFileWriteFuncPtr VirtualSoundFileWrite;
|
|
|
|
|
VirtualSoundFileTellFuncPtr VirtualSoundFileTell;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// SoundFile Constants
|
|
|
|
|
static const int32 SET_ENCODING_QUALITY = 0x1300;
|
|
|
|
|
static const int32 SET_CHANNEL_MAP_INFO = 0x1101;
|
|
|
|
|
static const int32 GET_CHANNEL_MAP_INFO = 0x1100;
|
|
|
|
|
|
|
|
|
|
// Exported SoundFile Functions
|
|
|
|
|
typedef LibSoundFileHandle*(*SoundFileOpenFuncPtr)(const char* Path, int32 Mode, FSoundFileDescription* Description);
|
|
|
|
|
typedef LibSoundFileHandle*(*SoundFileOpenVirtualFuncPtr)(FVirtualSoundFileCallbackInfo* VirtualFileDescription, int32 Mode, FSoundFileDescription* Description, void* UserData);
|
|
|
|
|
typedef int32(*SoundFileCloseFuncPtr)(LibSoundFileHandle* FileHandle);
|
|
|
|
|
typedef int32(*SoundFileErrorFuncPtr)(LibSoundFileHandle* FileHandle);
|
|
|
|
|
typedef const char*(*SoundFileStrErrorFuncPtr)(LibSoundFileHandle* FileHandle);
|
|
|
|
|
typedef const char*(*SoundFileErrorNumberFuncPtr)(int32 ErrorNumber);
|
|
|
|
|
typedef int32(*SoundFileCommandFuncPtr)(LibSoundFileHandle* FileHandle, int32 Command, void* Data, int32 DataSize);
|
|
|
|
|
typedef int32(*SoundFileFormatCheckFuncPtr)(const FSoundFileDescription* Description);
|
|
|
|
|
typedef SoundFileCount(*SoundFileSeekFuncPtr)(LibSoundFileHandle* FileHandle, SoundFileCount NumFrames, int32 SeekMode);
|
|
|
|
|
typedef const char*(*SoundFileGetVersionFuncPtr)(void);
|
|
|
|
|
typedef SoundFileCount(*SoundFileReadFramesFloatFuncPtr)(LibSoundFileHandle* FileHandle, float* Buffer, SoundFileCount NumFrames);
|
|
|
|
|
typedef SoundFileCount(*SoundFileReadFramesDoubleFuncPtr)(LibSoundFileHandle* FileHandle, double* Buffer, SoundFileCount NumFrames);
|
|
|
|
|
typedef SoundFileCount(*SoundFileWriteFramesFloatFuncPtr)(LibSoundFileHandle* FileHandle, const float* Buffer, SoundFileCount NumFrames);
|
|
|
|
|
typedef SoundFileCount(*SoundFileWriteFramesDoubleFuncPtr)(LibSoundFileHandle* FileHandle, const double* Buffer, SoundFileCount NumFrames);
|
|
|
|
|
typedef SoundFileCount(*SoundFileReadSamplesFloatFuncPtr)(LibSoundFileHandle* FileHandle, float* Buffer, SoundFileCount NumSamples);
|
|
|
|
|
typedef SoundFileCount(*SoundFileReadSamplesDoubleFuncPtr)(LibSoundFileHandle* FileHandle, double* Buffer, SoundFileCount NumSamples);
|
|
|
|
|
typedef SoundFileCount(*SoundFileWriteSamplesFloatFuncPtr)(LibSoundFileHandle* FileHandle, const float* Buffer, SoundFileCount NumSamples);
|
|
|
|
|
typedef SoundFileCount(*SoundFileWriteSamplesDoubleFuncPtr)(LibSoundFileHandle* FileHandle, const double* Buffer, SoundFileCount NumSamples);
|
|
|
|
|
|
|
|
|
|
SoundFileOpenFuncPtr SoundFileOpen = nullptr;
|
|
|
|
|
SoundFileOpenVirtualFuncPtr SoundFileOpenVirtual = nullptr;
|
|
|
|
|
SoundFileCloseFuncPtr SoundFileClose = nullptr;
|
|
|
|
|
SoundFileErrorFuncPtr SoundFileError = nullptr;
|
|
|
|
|
SoundFileStrErrorFuncPtr SoundFileStrError = nullptr;
|
|
|
|
|
SoundFileErrorNumberFuncPtr SoundFileErrorNumber = nullptr;
|
|
|
|
|
SoundFileCommandFuncPtr SoundFileCommand = nullptr;
|
|
|
|
|
SoundFileFormatCheckFuncPtr SoundFileFormatCheck = nullptr;
|
|
|
|
|
SoundFileSeekFuncPtr SoundFileSeek = nullptr;
|
|
|
|
|
SoundFileGetVersionFuncPtr SoundFileGetVersion = nullptr;
|
|
|
|
|
SoundFileReadFramesFloatFuncPtr SoundFileReadFramesFloat = nullptr;
|
|
|
|
|
SoundFileReadFramesDoubleFuncPtr SoundFileReadFramesDouble = nullptr;
|
|
|
|
|
SoundFileWriteFramesFloatFuncPtr SoundFileWriteFramesFloat = nullptr;
|
|
|
|
|
SoundFileWriteFramesDoubleFuncPtr SoundFileWriteFramesDouble = nullptr;
|
|
|
|
|
SoundFileReadSamplesFloatFuncPtr SoundFileReadSamplesFloat = nullptr;
|
|
|
|
|
SoundFileReadSamplesDoubleFuncPtr SoundFileReadSamplesDouble = nullptr;
|
|
|
|
|
SoundFileWriteSamplesFloatFuncPtr SoundFileWriteSamplesFloat = nullptr;
|
|
|
|
|
SoundFileWriteSamplesDoubleFuncPtr SoundFileWriteSamplesDouble = nullptr;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
Function implementations of virtual function callbacks
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
static SoundFileCount OnSoundFileGetLengthBytes(void* UserData)
|
|
|
|
|
{
|
|
|
|
|
SoundFileCount Length = 0;
|
|
|
|
|
((ISoundFileParser*)UserData)->GetLengthBytes(Length);
|
|
|
|
|
return Length;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static SoundFileCount OnSoundFileSeekBytes(SoundFileCount Offset, int32 Mode, void* UserData)
|
|
|
|
|
{
|
|
|
|
|
SoundFileCount OutOffset = 0;
|
|
|
|
|
((ISoundFileParser*)UserData)->SeekBytes(Offset, (ESoundFileSeekMode::Type)Mode, OutOffset);
|
|
|
|
|
return OutOffset;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static SoundFileCount OnSoundFileReadBytes(void* DataPtr, SoundFileCount ByteCount, void* UserData)
|
|
|
|
|
{
|
|
|
|
|
SoundFileCount OutBytesRead = 0;
|
|
|
|
|
((ISoundFileParser*)UserData)->ReadBytes(DataPtr, ByteCount, OutBytesRead);
|
|
|
|
|
return OutBytesRead;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static SoundFileCount OnSoundFileWriteBytes(const void* DataPtr, SoundFileCount ByteCount, void* UserData)
|
|
|
|
|
{
|
|
|
|
|
SoundFileCount OutBytesWritten = 0;
|
|
|
|
|
((ISoundFileParser*)UserData)->WriteBytes(DataPtr, ByteCount, OutBytesWritten);
|
|
|
|
|
return OutBytesWritten;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static SoundFileCount OnSoundFileTell(void* UserData)
|
|
|
|
|
{
|
|
|
|
|
SoundFileCount OutOffset = 0;
|
|
|
|
|
((ISoundFileParser*)UserData)->GetOffsetBytes(OutOffset);
|
|
|
|
|
return OutOffset;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Gets the default channel mapping for the given channel number
|
|
|
|
|
*/
|
|
|
|
|
static void GetDefaultMappingsForChannelNumber(int32 NumChannels, TArray<ESoundFileChannelMap::Type>& ChannelMap)
|
|
|
|
|
{
|
|
|
|
|
check(ChannelMap.Num() == NumChannels);
|
|
|
|
|
|
|
|
|
|
switch (NumChannels)
|
|
|
|
|
{
|
|
|
|
|
case 1: // MONO
|
|
|
|
|
ChannelMap[0] = ESoundFileChannelMap::MONO;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 2: // STEREO
|
|
|
|
|
ChannelMap[0] = ESoundFileChannelMap::LEFT;
|
|
|
|
|
ChannelMap[1] = ESoundFileChannelMap::RIGHT;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 3: // 2.1
|
|
|
|
|
ChannelMap[0] = ESoundFileChannelMap::LEFT;
|
|
|
|
|
ChannelMap[1] = ESoundFileChannelMap::RIGHT;
|
|
|
|
|
ChannelMap[2] = ESoundFileChannelMap::LFE;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 4: // Quadraphonic
|
|
|
|
|
ChannelMap[0] = ESoundFileChannelMap::LEFT;
|
|
|
|
|
ChannelMap[1] = ESoundFileChannelMap::RIGHT;
|
|
|
|
|
ChannelMap[2] = ESoundFileChannelMap::BACK_LEFT;
|
|
|
|
|
ChannelMap[3] = ESoundFileChannelMap::BACK_RIGHT;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 5: // 5.0
|
|
|
|
|
ChannelMap[0] = ESoundFileChannelMap::LEFT;
|
|
|
|
|
ChannelMap[1] = ESoundFileChannelMap::RIGHT;
|
|
|
|
|
ChannelMap[2] = ESoundFileChannelMap::CENTER;
|
|
|
|
|
ChannelMap[3] = ESoundFileChannelMap::SIDE_LEFT;
|
|
|
|
|
ChannelMap[4] = ESoundFileChannelMap::SIDE_RIGHT;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 6: // 5.1
|
|
|
|
|
ChannelMap[0] = ESoundFileChannelMap::LEFT;
|
|
|
|
|
ChannelMap[1] = ESoundFileChannelMap::RIGHT;
|
|
|
|
|
ChannelMap[2] = ESoundFileChannelMap::CENTER;
|
|
|
|
|
ChannelMap[3] = ESoundFileChannelMap::LFE;
|
|
|
|
|
ChannelMap[4] = ESoundFileChannelMap::SIDE_LEFT;
|
|
|
|
|
ChannelMap[5] = ESoundFileChannelMap::SIDE_RIGHT;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 7: // 6.1
|
|
|
|
|
ChannelMap[0] = ESoundFileChannelMap::LEFT;
|
|
|
|
|
ChannelMap[1] = ESoundFileChannelMap::RIGHT;
|
|
|
|
|
ChannelMap[2] = ESoundFileChannelMap::CENTER;
|
|
|
|
|
ChannelMap[3] = ESoundFileChannelMap::LFE;
|
|
|
|
|
ChannelMap[4] = ESoundFileChannelMap::SIDE_LEFT;
|
|
|
|
|
ChannelMap[5] = ESoundFileChannelMap::SIDE_RIGHT;
|
|
|
|
|
ChannelMap[6] = ESoundFileChannelMap::BACK_CENTER;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 8: // 7.1
|
|
|
|
|
ChannelMap[0] = ESoundFileChannelMap::LEFT;
|
|
|
|
|
ChannelMap[1] = ESoundFileChannelMap::RIGHT;
|
|
|
|
|
ChannelMap[2] = ESoundFileChannelMap::CENTER;
|
|
|
|
|
ChannelMap[3] = ESoundFileChannelMap::LFE;
|
|
|
|
|
ChannelMap[4] = ESoundFileChannelMap::BACK_LEFT;
|
|
|
|
|
ChannelMap[5] = ESoundFileChannelMap::BACK_RIGHT;
|
|
|
|
|
ChannelMap[6] = ESoundFileChannelMap::SIDE_LEFT;
|
|
|
|
|
ChannelMap[7] = ESoundFileChannelMap::SIDE_RIGHT;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static ESoundFileError::Type GetSoundDesriptionInternal(LibSoundFileHandle** OutFileHandle, const FString& FilePath, FSoundFileDescription& OutputDescription, TArray<ESoundFileChannelMap::Type>& OutChannelMap)
|
|
|
|
|
{
|
|
|
|
|
*OutFileHandle = nullptr;
|
|
|
|
|
|
|
|
|
|
// Check to see if the file exists
|
|
|
|
|
if (!FPaths::FileExists(FilePath))
|
|
|
|
|
{
|
|
|
|
|
UE_LOG(LogUnrealAudio, Error, TEXT("Sound file %s doesn't exist."), *FilePath);
|
|
|
|
|
return ESoundFileError::FILE_DOESNT_EXIST;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// open a sound file handle to get the description
|
|
|
|
|
*OutFileHandle = SoundFileOpen(TCHAR_TO_ANSI(*FilePath), ESoundFileOpenMode::READING, &OutputDescription);
|
|
|
|
|
if (!*OutFileHandle)
|
|
|
|
|
{
|
|
|
|
|
FString StrError = FString(SoundFileStrError(nullptr));
|
|
|
|
|
UE_LOG(LogUnrealAudio, Error, TEXT("Failed to open sound file %s: %s"), *FilePath, *StrError);
|
|
|
|
|
return ESoundFileError::FAILED_TO_OPEN;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Try to get a channel mapping
|
|
|
|
|
int32 NumChannels = OutputDescription.NumChannels;
|
|
|
|
|
OutChannelMap.Init(ESoundFileChannelMap::INVALID, NumChannels);
|
|
|
|
|
|
|
|
|
|
int32 Result = SoundFileCommand(*OutFileHandle, GET_CHANNEL_MAP_INFO, (int32*)OutChannelMap.GetData(), sizeof(int32)*NumChannels);
|
|
|
|
|
|
|
|
|
|
// If we failed to get the file's channel map definition, then we set the default based on the number of channels
|
|
|
|
|
if (Result == 0)
|
|
|
|
|
{
|
|
|
|
|
GetDefaultMappingsForChannelNumber(NumChannels, OutChannelMap);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// Check to see if the channel map we did get back is filled with INVALID channels
|
|
|
|
|
bool bIsInvalid = false;
|
|
|
|
|
for (ESoundFileChannelMap::Type ChannelType : OutChannelMap)
|
|
|
|
|
{
|
|
|
|
|
if (ChannelType == ESoundFileChannelMap::INVALID)
|
|
|
|
|
{
|
|
|
|
|
bIsInvalid = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// If invalid, then we need to get the default channel mapping
|
|
|
|
|
if (bIsInvalid)
|
|
|
|
|
{
|
|
|
|
|
GetDefaultMappingsForChannelNumber(NumChannels, OutChannelMap);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ESoundFileError::NONE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/************************************************************************/
|
|
|
|
|
/* FSoundFileReader */
|
|
|
|
|
/************************************************************************/
|
|
|
|
|
|
|
|
|
|
FSoundFileReader::FSoundFileReader(FUnrealAudioModule* InAudioModule)
|
|
|
|
|
: AudioModule(InAudioModule)
|
|
|
|
|
, CurrentIndexBytes(0)
|
|
|
|
|
, FileHandle(nullptr)
|
|
|
|
|
, State(ESoundFileState::UNINITIALIZED)
|
|
|
|
|
, CurrentError(ESoundFileError::NONE)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FSoundFileReader::~FSoundFileReader()
|
|
|
|
|
{
|
|
|
|
|
Release();
|
|
|
|
|
check(FileHandle == nullptr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ESoundFileError::Type FSoundFileReader::GetLengthBytes(SoundFileCount& OutLength) const
|
|
|
|
|
{
|
|
|
|
|
if (!SoundFileData.IsValid())
|
|
|
|
|
{
|
|
|
|
|
return ESoundFileError::INVALID_DATA;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int32 DataSize;
|
|
|
|
|
ESoundFileError::Type Error = SoundFileData->GetDataSize(DataSize);
|
|
|
|
|
if (Error == ESoundFileError::NONE)
|
|
|
|
|
{
|
|
|
|
|
OutLength = DataSize;
|
|
|
|
|
return ESoundFileError::NONE;
|
|
|
|
|
}
|
|
|
|
|
return Error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ESoundFileError::Type FSoundFileReader::SeekBytes(SoundFileCount Offset, ESoundFileSeekMode::Type SeekMode, SoundFileCount& OutOffset)
|
|
|
|
|
{
|
|
|
|
|
if (!SoundFileData.IsValid())
|
|
|
|
|
{
|
|
|
|
|
return ESoundFileError::INVALID_DATA;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int32 DataSize;
|
|
|
|
|
ESoundFileError::Type Error = SoundFileData->GetDataSize(DataSize);
|
|
|
|
|
if (Error != ESoundFileError::NONE)
|
|
|
|
|
{
|
|
|
|
|
return Error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SoundFileCount MaxBytes = DataSize;
|
|
|
|
|
if (MaxBytes == 0)
|
|
|
|
|
{
|
|
|
|
|
OutOffset = 0;
|
|
|
|
|
CurrentIndexBytes = 0;
|
|
|
|
|
return ESoundFileError::NONE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (SeekMode)
|
|
|
|
|
{
|
|
|
|
|
case ESoundFileSeekMode::FROM_START:
|
|
|
|
|
CurrentIndexBytes = Offset;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case ESoundFileSeekMode::FROM_CURRENT:
|
|
|
|
|
CurrentIndexBytes += Offset;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case ESoundFileSeekMode::FROM_END:
|
|
|
|
|
CurrentIndexBytes = MaxBytes + Offset;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
checkf(false, TEXT("Uknown seek mode!"));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Wrap the byte index to fall between 0 and MaxBytes
|
|
|
|
|
while (CurrentIndexBytes < 0)
|
|
|
|
|
{
|
|
|
|
|
CurrentIndexBytes += MaxBytes;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while (CurrentIndexBytes > MaxBytes)
|
|
|
|
|
{
|
|
|
|
|
CurrentIndexBytes -= MaxBytes;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
OutOffset = CurrentIndexBytes;
|
|
|
|
|
return ESoundFileError::NONE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ESoundFileError::Type FSoundFileReader::ReadBytes(void* DataPtr, SoundFileCount NumBytes, SoundFileCount& OutNumBytesRead)
|
|
|
|
|
{
|
|
|
|
|
if (!SoundFileData.IsValid())
|
|
|
|
|
{
|
|
|
|
|
return ESoundFileError::INVALID_DATA;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SoundFileCount EndByte = CurrentIndexBytes + NumBytes;
|
|
|
|
|
|
|
|
|
|
int32 DataSize;
|
|
|
|
|
ESoundFileError::Type Error = SoundFileData->GetDataSize(DataSize);
|
|
|
|
|
if (Error != ESoundFileError::NONE)
|
|
|
|
|
{
|
|
|
|
|
return Error;
|
|
|
|
|
}
|
|
|
|
|
SoundFileCount MaxBytes = DataSize;
|
|
|
|
|
if (EndByte >= MaxBytes)
|
|
|
|
|
{
|
|
|
|
|
NumBytes = MaxBytes - CurrentIndexBytes;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (NumBytes > 0)
|
|
|
|
|
{
|
|
|
|
|
TArray<uint8>* BulkData = nullptr;
|
|
|
|
|
Error = SoundFileData->GetBulkData(&BulkData);
|
|
|
|
|
if (Error != ESoundFileError::NONE)
|
|
|
|
|
{
|
|
|
|
|
return Error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
check(BulkData != nullptr);
|
|
|
|
|
|
|
|
|
|
FMemory::Memcpy(DataPtr, (const void*)&(*BulkData)[CurrentIndexBytes], NumBytes);
|
|
|
|
|
CurrentIndexBytes += NumBytes;
|
|
|
|
|
}
|
|
|
|
|
OutNumBytesRead = NumBytes;
|
|
|
|
|
return ESoundFileError::NONE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ESoundFileError::Type FSoundFileReader::WriteBytes(const void* DataPtr, SoundFileCount NumBytes, SoundFileCount& OutNumBytesWritten)
|
|
|
|
|
{
|
|
|
|
|
// This should never get called in the reader class
|
|
|
|
|
check(false);
|
|
|
|
|
return ESoundFileError::NONE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ESoundFileError::Type FSoundFileReader::GetOffsetBytes(SoundFileCount& OutOffset) const
|
|
|
|
|
{
|
|
|
|
|
OutOffset = CurrentIndexBytes;
|
|
|
|
|
return ESoundFileError::NONE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ESoundFileError::Type FSoundFileReader::Init(TSharedPtr<ISoundFile> InSoundFileData, bool bIsStreamed)
|
|
|
|
|
{
|
|
|
|
|
if (bIsStreamed)
|
|
|
|
|
{
|
|
|
|
|
return InitStreamed(InSoundFileData);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
return InitLoaded(InSoundFileData);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ESoundFileError::Type FSoundFileReader::Release()
|
|
|
|
|
{
|
|
|
|
|
if (FileHandle)
|
|
|
|
|
{
|
|
|
|
|
SoundFileClose(FileHandle);
|
|
|
|
|
FileHandle = nullptr;
|
|
|
|
|
}
|
|
|
|
|
return ESoundFileError::NONE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ESoundFileError::Type FSoundFileReader::SeekFrames(SoundFileCount Offset, ESoundFileSeekMode::Type SeekMode, SoundFileCount& OutOffset)
|
|
|
|
|
{
|
|
|
|
|
SoundFileCount Pos = SoundFileSeek(FileHandle, Offset, (int32)SeekMode);
|
|
|
|
|
if (Pos == -1)
|
|
|
|
|
{
|
|
|
|
|
FString StrErr = SoundFileStrError(FileHandle);
|
|
|
|
|
UE_LOG(LogUnrealAudio, Error, TEXT("Failed to seek file: %s"), *StrErr);
|
|
|
|
|
return SetError(ESoundFileError::FAILED_TO_SEEK);
|
|
|
|
|
}
|
|
|
|
|
return ESoundFileError::NONE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ESoundFileError::Type FSoundFileReader::ReadFrames(float* DataPtr, SoundFileCount NumFrames, SoundFileCount& OutNumFramesRead)
|
|
|
|
|
{
|
|
|
|
|
OutNumFramesRead = SoundFileReadFramesFloat(FileHandle, DataPtr, NumFrames);
|
|
|
|
|
return ESoundFileError::NONE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ESoundFileError::Type FSoundFileReader::ReadFrames(double* DataPtr, SoundFileCount NumFrames, SoundFileCount& OutNumFramesRead)
|
|
|
|
|
{
|
|
|
|
|
OutNumFramesRead = SoundFileReadFramesDouble(FileHandle, DataPtr, NumFrames);
|
|
|
|
|
return ESoundFileError::NONE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ESoundFileError::Type FSoundFileReader::ReadSamples(float* DataPtr, SoundFileCount NumSamples, SoundFileCount& OutNumSamplesRead)
|
|
|
|
|
{
|
|
|
|
|
OutNumSamplesRead = SoundFileReadSamplesFloat(FileHandle, DataPtr, NumSamples);
|
|
|
|
|
return ESoundFileError::NONE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ESoundFileError::Type FSoundFileReader::ReadSamples(double* DataPtr, SoundFileCount NumSamples, SoundFileCount& OutNumSamplesRead)
|
|
|
|
|
{
|
|
|
|
|
OutNumSamplesRead = SoundFileReadSamplesDouble(FileHandle, DataPtr, NumSamples);
|
|
|
|
|
return ESoundFileError::NONE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ESoundFileError::Type FSoundFileReader::InitLoaded(TSharedPtr<ISoundFile> InSoundFileData)
|
|
|
|
|
{
|
|
|
|
|
if (!(State.GetValue() == ESoundFileState::UNINITIALIZED || State.GetValue() == ESoundFileState::LOADING))
|
|
|
|
|
{
|
Copying //UE4/Dev-Framework to Dev-Main (//UE4/Dev-Main) @ 2944217
#lockdown Nick.Penwarden
==========================
MAJOR FEATURES + CHANGES
==========================
Change 2899855 on 2016/03/08 by Marc.Audy
Merging //UE4/Dev-Main to Dev-Framework (//UE4/Dev-Framework) @ 2899785
Change 2926689 on 2016/03/29 by Jeff.Farris
AAIController::SetFocus() will now implicitly clear any location focus at the same priority.
UE-27975
#rb john.abercrombie
Change 2926690 on 2016/03/29 by Jeff.Farris
Using wildcard operator with the "KismetEvent" or "ke" console commands will now only trigger the event on objects in the world in which it was triggered. Prevents badness with running events on things like CDOs and editor actors. (UE-23106)
Change 2926691 on 2016/03/29 by mason.seay
Content for testing collision on scaled components
Change 2926692 on 2016/03/29 by Jeff.Farris
- FixupDeltaSeconds now considers time dilation when clamping.
- Acceptable range for time dilation values is now a config parameter on WorldSettings
- Acceptable range for undilated frame times is now a config parameter on WorldSettings
(UE-27815)
#rb marc.audy
Change 2926711 on 2016/03/29 by Ori.Cohen
Fix constraint rendering when scaling a cosntraint actor
#JIRA UE-28691, UE-28700
#rb Lina.Halper
Change 2926745 on 2016/03/29 by Lukasz.Furman
navigation filters can now be instantiated per querier - usually AI agent
required for FORT-21372
Change 2926789 on 2016/03/29 by Ori.Cohen
Downgrade check to ensure for 2d physics during a hard shutdown
#rb Michael.Noland
Change 2926859 on 2016/03/29 by Ori.Cohen
Fix red herring warnings of not locking physx scenes during hard shutdown.
#JIRA UE-28747
#rb Michael.Noland
Change 2927444 on 2016/03/30 by Thomas.Sarkanen
Fixed Blueprint compiler errors when resetting timer handles
Added basic support for 64-bit int/uint terms to Blueprint. This allows the use of opaque 64-bit integer types inside of BlueprintType structs, it in no way means that 64-bit ints are fully supported in Blueprint.
Corrected a left-over formatting oversight when converting a FTimerHandle to a string.
Added new by-ref "Clear and Invalidate Timer by Handle" function to Blueprint system library & deprecated old version.
#rb Maciej.Mroz (and a few others!)
#jira UE-28833 - Unresolved compiler error for B_Pickups blueprint in Fortnite
Change 2927520 on 2016/03/30 by Jurre.deBaare
Should not allow skeletal mesh components mobility to be set to static, but detach instead
#fix Added CanHaveStaticMobility to SceneComponent class, and check this when trying to propogate Static mobility to parent component
#jira UE-26364
Change 2927533 on 2016/03/30 by Jurre.deBaare
Static Mesh Merge tool: when merging from multiple blueprints, fails to combine same materials
#fix Material index remapping was part of if-clause where it shouldn't be
#jira UE-23827
Static Mesh Merge tool, failed to combine physics data if using complex
#fix Required copying the SectionInfoMap from source static meshes
HLOD/MergeActor - Vertex Colours are not correctly propagated to negatively scaled meshes
#fix had to re-order function calls
#jira UE-28316
#rb James.Golding
Change 2927535 on 2016/03/30 by Ori.Cohen
Make sub-stepping run on game thread
#JIRA UE-24011
#rb Gil.Gribb
Change 2927537 on 2016/03/30 by Jurre.deBaare
Warning message when HLOD mesh > 65536 vertices
#jira UE-22365
#fix added messages when building proxy mesh
Change 2927691 on 2016/03/30 by Jeff.Farris
Fixed potential PlayerState leak (UE-22700)
Change 2927692 on 2016/03/30 by Lina.Halper
Allow it to select any name they want other than just restrict to what we have.
- I think it may not be the best solution but with current widget built, you can't even clear name, which is problem.
- Other solution is to add "Clear" as a name, and when that gets entered, we just clear it, but then the X button is odd and no purpose being there.
- I think we should just allow them to choose if they don't like it but with suggestions.
#rb: Ori.Cohen
#jira UE-27786
#code review: Benn.Gallagher
Change 2927853 on 2016/03/30 by Lina.Halper
[CL 2944273 by Marc Audy in Main branch]
2016-04-14 16:25:11 -04:00
|
|
|
return SetError(ESoundFileError::ALREADY_INITIALIZED);
|
2015-07-14 13:35:50 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
check(InSoundFileData.IsValid());
|
|
|
|
|
check(FileHandle == nullptr);
|
|
|
|
|
|
|
|
|
|
// Setting sound file data initializes this sound file
|
|
|
|
|
SoundFileData = InSoundFileData;
|
|
|
|
|
check(SoundFileData.IsValid());
|
|
|
|
|
|
|
|
|
|
bool bIsStreamed;
|
|
|
|
|
ESoundFileError::Type Error = SoundFileData->IsStreamed(bIsStreamed);
|
|
|
|
|
if (Error != ESoundFileError::NONE)
|
|
|
|
|
{
|
|
|
|
|
return Error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (bIsStreamed)
|
|
|
|
|
{
|
|
|
|
|
return ESoundFileError::INVALID_DATA;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ESoundFileState::Type SoundFileState;
|
|
|
|
|
Error = SoundFileData->GetState(SoundFileState);
|
|
|
|
|
if (Error != ESoundFileError::NONE)
|
|
|
|
|
{
|
|
|
|
|
return Error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (SoundFileState != ESoundFileState::LOADED)
|
|
|
|
|
{
|
|
|
|
|
return ESoundFileError::INVALID_STATE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Open up a virtual file handle with this data
|
|
|
|
|
FVirtualSoundFileCallbackInfo VirtualSoundFileInfo;
|
|
|
|
|
VirtualSoundFileInfo.VirtualSoundFileGetLength = OnSoundFileGetLengthBytes;
|
|
|
|
|
VirtualSoundFileInfo.VirtualSoundFileSeek = OnSoundFileSeekBytes;
|
|
|
|
|
VirtualSoundFileInfo.VirtualSoundFileRead = OnSoundFileReadBytes;
|
|
|
|
|
VirtualSoundFileInfo.VirtualSoundFileWrite = OnSoundFileWriteBytes;
|
|
|
|
|
VirtualSoundFileInfo.VirtualSoundFileTell = OnSoundFileTell;
|
|
|
|
|
|
|
|
|
|
FSoundFileDescription Description;
|
|
|
|
|
SoundFileData->GetDescription(Description);
|
|
|
|
|
if (!SoundFileFormatCheck(&Description))
|
|
|
|
|
{
|
|
|
|
|
return SetError(ESoundFileError::INVALID_INPUT_FORMAT);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FileHandle = SoundFileOpenVirtual(&VirtualSoundFileInfo, ESoundFileOpenMode::READING, &Description, (void*)this);
|
|
|
|
|
if (!FileHandle)
|
|
|
|
|
{
|
|
|
|
|
FString StrErr = SoundFileStrError(nullptr);
|
|
|
|
|
UE_LOG(LogUnrealAudio, Error, TEXT("Failed to intitialize sound file: %s"), *StrErr);
|
|
|
|
|
return SetError(ESoundFileError::FAILED_TO_OPEN);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
State.Set(ESoundFileState::INITIALIZED);
|
|
|
|
|
return ESoundFileError::NONE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ESoundFileError::Type FSoundFileReader::InitStreamed(TSharedPtr<ISoundFile> InSoundFileData)
|
|
|
|
|
{
|
|
|
|
|
if (!(State.GetValue() == ESoundFileState::UNINITIALIZED || State.GetValue() == ESoundFileState::LOADING))
|
|
|
|
|
{
|
Copying //UE4/Dev-Framework to Dev-Main (//UE4/Dev-Main) @ 2944217
#lockdown Nick.Penwarden
==========================
MAJOR FEATURES + CHANGES
==========================
Change 2899855 on 2016/03/08 by Marc.Audy
Merging //UE4/Dev-Main to Dev-Framework (//UE4/Dev-Framework) @ 2899785
Change 2926689 on 2016/03/29 by Jeff.Farris
AAIController::SetFocus() will now implicitly clear any location focus at the same priority.
UE-27975
#rb john.abercrombie
Change 2926690 on 2016/03/29 by Jeff.Farris
Using wildcard operator with the "KismetEvent" or "ke" console commands will now only trigger the event on objects in the world in which it was triggered. Prevents badness with running events on things like CDOs and editor actors. (UE-23106)
Change 2926691 on 2016/03/29 by mason.seay
Content for testing collision on scaled components
Change 2926692 on 2016/03/29 by Jeff.Farris
- FixupDeltaSeconds now considers time dilation when clamping.
- Acceptable range for time dilation values is now a config parameter on WorldSettings
- Acceptable range for undilated frame times is now a config parameter on WorldSettings
(UE-27815)
#rb marc.audy
Change 2926711 on 2016/03/29 by Ori.Cohen
Fix constraint rendering when scaling a cosntraint actor
#JIRA UE-28691, UE-28700
#rb Lina.Halper
Change 2926745 on 2016/03/29 by Lukasz.Furman
navigation filters can now be instantiated per querier - usually AI agent
required for FORT-21372
Change 2926789 on 2016/03/29 by Ori.Cohen
Downgrade check to ensure for 2d physics during a hard shutdown
#rb Michael.Noland
Change 2926859 on 2016/03/29 by Ori.Cohen
Fix red herring warnings of not locking physx scenes during hard shutdown.
#JIRA UE-28747
#rb Michael.Noland
Change 2927444 on 2016/03/30 by Thomas.Sarkanen
Fixed Blueprint compiler errors when resetting timer handles
Added basic support for 64-bit int/uint terms to Blueprint. This allows the use of opaque 64-bit integer types inside of BlueprintType structs, it in no way means that 64-bit ints are fully supported in Blueprint.
Corrected a left-over formatting oversight when converting a FTimerHandle to a string.
Added new by-ref "Clear and Invalidate Timer by Handle" function to Blueprint system library & deprecated old version.
#rb Maciej.Mroz (and a few others!)
#jira UE-28833 - Unresolved compiler error for B_Pickups blueprint in Fortnite
Change 2927520 on 2016/03/30 by Jurre.deBaare
Should not allow skeletal mesh components mobility to be set to static, but detach instead
#fix Added CanHaveStaticMobility to SceneComponent class, and check this when trying to propogate Static mobility to parent component
#jira UE-26364
Change 2927533 on 2016/03/30 by Jurre.deBaare
Static Mesh Merge tool: when merging from multiple blueprints, fails to combine same materials
#fix Material index remapping was part of if-clause where it shouldn't be
#jira UE-23827
Static Mesh Merge tool, failed to combine physics data if using complex
#fix Required copying the SectionInfoMap from source static meshes
HLOD/MergeActor - Vertex Colours are not correctly propagated to negatively scaled meshes
#fix had to re-order function calls
#jira UE-28316
#rb James.Golding
Change 2927535 on 2016/03/30 by Ori.Cohen
Make sub-stepping run on game thread
#JIRA UE-24011
#rb Gil.Gribb
Change 2927537 on 2016/03/30 by Jurre.deBaare
Warning message when HLOD mesh > 65536 vertices
#jira UE-22365
#fix added messages when building proxy mesh
Change 2927691 on 2016/03/30 by Jeff.Farris
Fixed potential PlayerState leak (UE-22700)
Change 2927692 on 2016/03/30 by Lina.Halper
Allow it to select any name they want other than just restrict to what we have.
- I think it may not be the best solution but with current widget built, you can't even clear name, which is problem.
- Other solution is to add "Clear" as a name, and when that gets entered, we just clear it, but then the X button is odd and no purpose being there.
- I think we should just allow them to choose if they don't like it but with suggestions.
#rb: Ori.Cohen
#jira UE-27786
#code review: Benn.Gallagher
Change 2927853 on 2016/03/30 by Lina.Halper
[CL 2944273 by Marc Audy in Main branch]
2016-04-14 16:25:11 -04:00
|
|
|
return SetError(ESoundFileError::ALREADY_INITIALIZED);
|
2015-07-14 13:35:50 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
check(InSoundFileData.IsValid());
|
|
|
|
|
check(FileHandle == nullptr);
|
|
|
|
|
|
|
|
|
|
// Setting sound file data initializes this sound file
|
|
|
|
|
SoundFileData = InSoundFileData;
|
|
|
|
|
check(SoundFileData.IsValid());
|
|
|
|
|
|
|
|
|
|
bool bIsStreamed;
|
|
|
|
|
ESoundFileError::Type Error = SoundFileData->IsStreamed(bIsStreamed);
|
|
|
|
|
if (Error != ESoundFileError::NONE)
|
|
|
|
|
{
|
|
|
|
|
return Error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!bIsStreamed)
|
|
|
|
|
{
|
|
|
|
|
return ESoundFileError::INVALID_DATA;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ESoundFileState::Type SoundFileState;
|
|
|
|
|
Error = SoundFileData->GetState(SoundFileState);
|
|
|
|
|
if (Error != ESoundFileError::NONE)
|
|
|
|
|
{
|
|
|
|
|
return Error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (SoundFileState != ESoundFileState::STREAMING)
|
|
|
|
|
{
|
|
|
|
|
return ESoundFileError::INVALID_STATE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FName NamePath;
|
|
|
|
|
Error = SoundFileData->GetPath(NamePath);
|
|
|
|
|
if (Error != ESoundFileError::NONE)
|
|
|
|
|
{
|
|
|
|
|
return Error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FString FilePath = NamePath.GetPlainNameString();
|
|
|
|
|
|
|
|
|
|
FSoundFileDescription Description;
|
|
|
|
|
TArray<ESoundFileChannelMap::Type> ChannelMap;
|
|
|
|
|
Error = GetSoundDesriptionInternal(&FileHandle, FilePath, Description, ChannelMap);
|
|
|
|
|
if (Error == ESoundFileError::NONE)
|
|
|
|
|
{
|
|
|
|
|
// Tell this reader that we're in streaming mode.
|
|
|
|
|
State.Set(ESoundFileState::STREAMING);
|
|
|
|
|
return ESoundFileError::NONE;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
return SetError(Error);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ESoundFileError::Type FSoundFileReader::SetError(ESoundFileError::Type InError)
|
|
|
|
|
{
|
|
|
|
|
if (InError != ESoundFileError::NONE)
|
|
|
|
|
{
|
|
|
|
|
State.Set(ESoundFileState::HAS_ERROR);
|
|
|
|
|
}
|
|
|
|
|
CurrentError.Set(InError);
|
|
|
|
|
return InError;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/************************************************************************/
|
|
|
|
|
/* FSoundFileWriter */
|
|
|
|
|
/************************************************************************/
|
|
|
|
|
|
|
|
|
|
FSoundFileWriter::FSoundFileWriter(FUnrealAudioModule* InAudioModule)
|
|
|
|
|
: AudioModule(InAudioModule)
|
|
|
|
|
, CurrentIndexBytes(0)
|
|
|
|
|
, FileHandle(nullptr)
|
|
|
|
|
, EncodingQuality(0.0)
|
|
|
|
|
, State(ESoundFileState::UNINITIALIZED)
|
|
|
|
|
, CurrentError(ESoundFileError::NONE)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FSoundFileWriter::~FSoundFileWriter()
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ESoundFileError::Type FSoundFileWriter::GetLengthBytes(SoundFileCount& OutLength) const
|
|
|
|
|
{
|
|
|
|
|
OutLength = BulkData.Num();
|
|
|
|
|
return ESoundFileError::NONE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ESoundFileError::Type FSoundFileWriter::SeekBytes(SoundFileCount Offset, ESoundFileSeekMode::Type SeekMode, SoundFileCount& OutOffset)
|
|
|
|
|
{
|
|
|
|
|
int32 DataSize = BulkData.Num();
|
|
|
|
|
|
|
|
|
|
SoundFileCount MaxBytes = DataSize;
|
|
|
|
|
if (MaxBytes == 0)
|
|
|
|
|
{
|
|
|
|
|
OutOffset = 0;
|
|
|
|
|
CurrentIndexBytes = 0;
|
|
|
|
|
return ESoundFileError::NONE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (SeekMode)
|
|
|
|
|
{
|
|
|
|
|
case ESoundFileSeekMode::FROM_START:
|
|
|
|
|
CurrentIndexBytes = Offset;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case ESoundFileSeekMode::FROM_CURRENT:
|
|
|
|
|
CurrentIndexBytes += Offset;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case ESoundFileSeekMode::FROM_END:
|
|
|
|
|
CurrentIndexBytes = MaxBytes + Offset;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
checkf(false, TEXT("Uknown seek mode!"));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Wrap the byte index to fall between 0 and MaxBytes
|
|
|
|
|
while (CurrentIndexBytes < 0)
|
|
|
|
|
{
|
|
|
|
|
CurrentIndexBytes += MaxBytes;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while (CurrentIndexBytes > MaxBytes)
|
|
|
|
|
{
|
|
|
|
|
CurrentIndexBytes -= MaxBytes;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
OutOffset = CurrentIndexBytes;
|
|
|
|
|
return ESoundFileError::NONE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ESoundFileError::Type FSoundFileWriter::ReadBytes(void* DataPtr, SoundFileCount NumBytes, SoundFileCount& OutNumBytesRead)
|
|
|
|
|
{
|
|
|
|
|
// This shouldn't get called in the writer
|
|
|
|
|
check(false);
|
|
|
|
|
return ESoundFileError::NONE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ESoundFileError::Type FSoundFileWriter::WriteBytes(const void* DataPtr, SoundFileCount NumBytes, SoundFileCount& OutNumBytesWritten)
|
|
|
|
|
{
|
|
|
|
|
const uint8* InDataBytes = (const uint8*)DataPtr;
|
|
|
|
|
|
|
|
|
|
SoundFileCount MaxBytes = BulkData.Num();
|
|
|
|
|
|
|
|
|
|
// Append the data to the buffer
|
|
|
|
|
if (CurrentIndexBytes == MaxBytes)
|
|
|
|
|
{
|
|
|
|
|
BulkData.Append(InDataBytes, NumBytes);
|
|
|
|
|
CurrentIndexBytes += NumBytes;
|
|
|
|
|
}
|
|
|
|
|
else if ((CurrentIndexBytes + NumBytes) < MaxBytes)
|
|
|
|
|
{
|
|
|
|
|
// Write over the existing data in the buffer
|
|
|
|
|
for (SoundFileCount i = 0; i < NumBytes; ++i)
|
|
|
|
|
{
|
|
|
|
|
BulkData[CurrentIndexBytes++] = InDataBytes[i];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// Overwrite some data until end of current buffer size
|
|
|
|
|
SoundFileCount RemainingBytes = MaxBytes - CurrentIndexBytes;
|
|
|
|
|
SoundFileCount InputBufferIndex = 0;
|
|
|
|
|
for (InputBufferIndex = 0; InputBufferIndex < RemainingBytes; ++InputBufferIndex)
|
|
|
|
|
{
|
|
|
|
|
BulkData[CurrentIndexBytes++] = InDataBytes[InputBufferIndex];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Now append the remainder of the input buffer to the internal data buffer
|
|
|
|
|
const uint8* RemainingData = &InDataBytes[InputBufferIndex];
|
|
|
|
|
RemainingBytes = NumBytes - RemainingBytes;
|
|
|
|
|
BulkData.Append(RemainingData, RemainingBytes);
|
|
|
|
|
CurrentIndexBytes += RemainingBytes;
|
|
|
|
|
check(CurrentIndexBytes == BulkData.Num());
|
|
|
|
|
}
|
|
|
|
|
OutNumBytesWritten = NumBytes;
|
|
|
|
|
return ESoundFileError::NONE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ESoundFileError::Type FSoundFileWriter::GetOffsetBytes(SoundFileCount& OutOffset) const
|
|
|
|
|
{
|
|
|
|
|
OutOffset = CurrentIndexBytes;
|
|
|
|
|
return ESoundFileError::NONE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ESoundFileError::Type FSoundFileWriter::Init(const FSoundFileDescription& InDescription, const TArray<ESoundFileChannelMap::Type>& InChannelMap, double InEncodingQuality)
|
|
|
|
|
{
|
|
|
|
|
State.Set(ESoundFileState::INITIALIZED);
|
|
|
|
|
|
|
|
|
|
BulkData.Reset();
|
|
|
|
|
Description = InDescription;
|
|
|
|
|
ChannelMap = InChannelMap;
|
|
|
|
|
EncodingQuality = InEncodingQuality;
|
|
|
|
|
|
|
|
|
|
// First check the input format to make sure it's valid
|
|
|
|
|
if (!SoundFileFormatCheck(&InDescription))
|
|
|
|
|
{
|
|
|
|
|
UE_LOG(LogUnrealAudio,
|
|
|
|
|
Error,
|
|
|
|
|
TEXT("Sound file input format (%s - %s) is invalid."),
|
|
|
|
|
ESoundFileFormat::ToStringMajor(InDescription.FormatFlags),
|
|
|
|
|
ESoundFileFormat::ToStringMinor(InDescription.FormatFlags));
|
|
|
|
|
|
|
|
|
|
return SetError(ESoundFileError::INVALID_INPUT_FORMAT);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Make sure we have the right number of channels and our channel map size
|
|
|
|
|
if (InChannelMap.Num() != InDescription.NumChannels)
|
|
|
|
|
{
|
|
|
|
|
UE_LOG(LogUnrealAudio, Error, TEXT("Channel map didn't match the input NumChannels"));
|
|
|
|
|
return SetError(ESoundFileError::INVALID_CHANNEL_MAP);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FVirtualSoundFileCallbackInfo VirtualSoundFileInfo;
|
|
|
|
|
VirtualSoundFileInfo.VirtualSoundFileGetLength = OnSoundFileGetLengthBytes;
|
|
|
|
|
VirtualSoundFileInfo.VirtualSoundFileSeek = OnSoundFileSeekBytes;
|
|
|
|
|
VirtualSoundFileInfo.VirtualSoundFileRead = OnSoundFileReadBytes;
|
|
|
|
|
VirtualSoundFileInfo.VirtualSoundFileWrite = OnSoundFileWriteBytes;
|
|
|
|
|
VirtualSoundFileInfo.VirtualSoundFileTell = OnSoundFileTell;
|
|
|
|
|
|
|
|
|
|
FileHandle = SoundFileOpenVirtual(&VirtualSoundFileInfo, ESoundFileOpenMode::WRITING, &Description, (void*)this);
|
|
|
|
|
if (!FileHandle)
|
|
|
|
|
{
|
|
|
|
|
FString StrErr = FString(SoundFileStrError(nullptr));
|
|
|
|
|
UE_LOG(LogUnrealAudio, Error, TEXT("Failed to open empty sound file: %s"), *StrErr);
|
|
|
|
|
return SetError(ESoundFileError::FAILED_TO_OPEN);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int32 Result = SoundFileCommand(FileHandle, SET_CHANNEL_MAP_INFO, (int32*)InChannelMap.GetData(), sizeof(int32)*Description.NumChannels);
|
|
|
|
|
if (Result)
|
|
|
|
|
{
|
|
|
|
|
FString StrErr = SoundFileStrError(nullptr);
|
|
|
|
|
UE_LOG(LogUnrealAudio, Error, TEXT("Failed to set the channel map on empty file for writing: %s"), *StrErr);
|
|
|
|
|
return SetError(ESoundFileError::INVALID_CHANNEL_MAP);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((Description.FormatFlags & ESoundFileFormat::MAJOR_FORMAT_MASK) == ESoundFileFormat::OGG)
|
|
|
|
|
{
|
2015-07-15 13:06:07 -04:00
|
|
|
int32 Result2 = SoundFileCommand(FileHandle, SET_ENCODING_QUALITY, &EncodingQuality, sizeof(double));
|
|
|
|
|
if (Result2 != 1)
|
2015-07-14 13:35:50 -04:00
|
|
|
{
|
|
|
|
|
FString StrErr = SoundFileStrError(FileHandle);
|
|
|
|
|
UE_LOG(LogUnrealAudio, Error, TEXT("Failed to set encoding quality: %s"), *StrErr);
|
|
|
|
|
return SetError(ESoundFileError::BAD_ENCODING_QUALITY);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ESoundFileError::NONE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ESoundFileError::Type FSoundFileWriter::Release()
|
|
|
|
|
{
|
|
|
|
|
if (FileHandle)
|
|
|
|
|
{
|
|
|
|
|
SoundFileClose(FileHandle);
|
|
|
|
|
FileHandle = nullptr;
|
|
|
|
|
}
|
|
|
|
|
return ESoundFileError::NONE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ESoundFileError::Type FSoundFileWriter::SeekFrames(SoundFileCount Offset, ESoundFileSeekMode::Type SeekMode, SoundFileCount& OutOffset)
|
|
|
|
|
{
|
|
|
|
|
SoundFileCount Pos = SoundFileSeek(FileHandle, Offset, (int32)SeekMode);
|
|
|
|
|
if (Pos == -1)
|
|
|
|
|
{
|
|
|
|
|
FString StrErr = SoundFileStrError(FileHandle);
|
|
|
|
|
UE_LOG(LogUnrealAudio, Error, TEXT("Failed to seek file: %s"), *StrErr);
|
|
|
|
|
return SetError(ESoundFileError::FAILED_TO_SEEK);
|
|
|
|
|
}
|
|
|
|
|
return ESoundFileError::NONE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ESoundFileError::Type FSoundFileWriter::WriteFrames(const float* DataPtr, SoundFileCount NumFrames, SoundFileCount& OutNumFramesWritten)
|
|
|
|
|
{
|
|
|
|
|
OutNumFramesWritten = SoundFileWriteFramesFloat(FileHandle, DataPtr, NumFrames);
|
|
|
|
|
return ESoundFileError::NONE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ESoundFileError::Type FSoundFileWriter::WriteFrames(const double* DataPtr, SoundFileCount NumFrames, SoundFileCount& OutNumFramesWritten)
|
|
|
|
|
{
|
|
|
|
|
OutNumFramesWritten = SoundFileWriteFramesDouble(FileHandle, DataPtr, NumFrames);
|
|
|
|
|
return ESoundFileError::NONE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ESoundFileError::Type FSoundFileWriter::WriteSamples(const float* DataPtr, SoundFileCount NumSamples, SoundFileCount& OutNumSampleWritten)
|
|
|
|
|
{
|
|
|
|
|
OutNumSampleWritten = SoundFileWriteSamplesFloat(FileHandle, DataPtr, NumSamples);
|
|
|
|
|
return ESoundFileError::NONE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ESoundFileError::Type FSoundFileWriter::WriteSamples(const double* DataPtr, SoundFileCount NumSamples, SoundFileCount& OutNumSampleWritten)
|
|
|
|
|
{
|
|
|
|
|
OutNumSampleWritten = SoundFileWriteSamplesDouble(FileHandle, DataPtr, NumSamples);
|
|
|
|
|
return ESoundFileError::NONE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ESoundFileError::Type FSoundFileWriter::GetData(TArray<uint8>** OutBulkData)
|
|
|
|
|
{
|
|
|
|
|
*OutBulkData = &BulkData;
|
|
|
|
|
return ESoundFileError::NONE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ESoundFileError::Type FSoundFileWriter::SetError(ESoundFileError::Type InError)
|
|
|
|
|
{
|
|
|
|
|
if (InError != ESoundFileError::NONE)
|
|
|
|
|
{
|
|
|
|
|
State.Set(ESoundFileState::HAS_ERROR);
|
|
|
|
|
}
|
|
|
|
|
CurrentError.Set(InError);
|
|
|
|
|
return InError;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/************************************************************************/
|
|
|
|
|
/* FUnrealAudioModule */
|
|
|
|
|
/************************************************************************/
|
|
|
|
|
|
|
|
|
|
static void* GetSoundFileDllHandle()
|
|
|
|
|
{
|
|
|
|
|
void* DllHandle = nullptr;
|
|
|
|
|
#if PLATFORM_WINDOWS
|
|
|
|
|
FString Path = FPaths::EngineDir() / FString(TEXT("Binaries/ThirdParty/libsndfile/Win64/"));
|
|
|
|
|
FPlatformProcess::PushDllDirectory(*Path);
|
|
|
|
|
DllHandle = FPlatformProcess::GetDllHandle(*(Path + "libsndfile-1.dll"));
|
|
|
|
|
FPlatformProcess::PopDllDirectory(*Path);
|
|
|
|
|
#elif PLATFORM_MAC
|
|
|
|
|
// FString Path = FPaths::EngineDir() / FString(TEXT("Binaries/ThirdParty/libsndfile/Mac/"));
|
|
|
|
|
// FPlatformProcess::PushDllDirectory(*Path);
|
|
|
|
|
DllHandle = FPlatformProcess::GetDllHandle(TEXT("libsndfile.1.dylib"));
|
|
|
|
|
// FPlatformProcess::PopDllDirectory(*Path);
|
|
|
|
|
#endif
|
|
|
|
|
return DllHandle;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool FUnrealAudioModule::LoadSoundFileLib()
|
|
|
|
|
{
|
|
|
|
|
SoundFileDllHandle = GetSoundFileDllHandle();
|
|
|
|
|
if (!SoundFileDllHandle)
|
|
|
|
|
{
|
|
|
|
|
UE_LOG(LogUnrealAudio, Error, TEXT("Failed to load Sound File dll"));
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool bSuccess = true;
|
|
|
|
|
|
|
|
|
|
void* LambdaDLLHandle = SoundFileDllHandle;
|
|
|
|
|
|
|
|
|
|
// Helper function to load DLL exports and report warnings
|
|
|
|
|
auto GetSoundFileDllExport = [&LambdaDLLHandle](const TCHAR* FuncName, bool& bInSuccess) -> void*
|
|
|
|
|
{
|
|
|
|
|
if (bInSuccess)
|
|
|
|
|
{
|
|
|
|
|
void* FuncPtr = FPlatformProcess::GetDllExport(LambdaDLLHandle, FuncName);
|
|
|
|
|
if (FuncPtr == nullptr)
|
|
|
|
|
{
|
|
|
|
|
bInSuccess = false;
|
|
|
|
|
UE_LOG(LogUnrealAudio, Warning, TEXT("Failed to locate the expected DLL import function '%s' in the SoundFile DLL."), *FuncName);
|
|
|
|
|
FPlatformProcess::FreeDllHandle(LambdaDLLHandle);
|
|
|
|
|
LambdaDLLHandle = nullptr;
|
|
|
|
|
}
|
|
|
|
|
return FuncPtr;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
SoundFileOpen = (SoundFileOpenFuncPtr)GetSoundFileDllExport(TEXT("sf_open"), bSuccess);
|
|
|
|
|
SoundFileOpenVirtual = (SoundFileOpenVirtualFuncPtr)GetSoundFileDllExport(TEXT("sf_open_virtual"), bSuccess);
|
|
|
|
|
SoundFileClose = (SoundFileCloseFuncPtr)GetSoundFileDllExport(TEXT("sf_close"), bSuccess);
|
|
|
|
|
SoundFileError = (SoundFileErrorFuncPtr)GetSoundFileDllExport(TEXT("sf_error"), bSuccess);
|
|
|
|
|
SoundFileStrError = (SoundFileStrErrorFuncPtr)GetSoundFileDllExport(TEXT("sf_strerror"), bSuccess);
|
|
|
|
|
SoundFileErrorNumber = (SoundFileErrorNumberFuncPtr)GetSoundFileDllExport(TEXT("sf_error_number"), bSuccess);
|
|
|
|
|
SoundFileCommand = (SoundFileCommandFuncPtr)GetSoundFileDllExport(TEXT("sf_command"), bSuccess);
|
|
|
|
|
SoundFileFormatCheck = (SoundFileFormatCheckFuncPtr)GetSoundFileDllExport(TEXT("sf_format_check"), bSuccess);
|
|
|
|
|
SoundFileSeek = (SoundFileSeekFuncPtr)GetSoundFileDllExport(TEXT("sf_seek"), bSuccess);
|
|
|
|
|
SoundFileGetVersion = (SoundFileGetVersionFuncPtr)GetSoundFileDllExport(TEXT("sf_version_string"), bSuccess);
|
|
|
|
|
SoundFileReadFramesFloat = (SoundFileReadFramesFloatFuncPtr)GetSoundFileDllExport(TEXT("sf_readf_float"), bSuccess);
|
|
|
|
|
SoundFileReadFramesDouble = (SoundFileReadFramesDoubleFuncPtr)GetSoundFileDllExport(TEXT("sf_readf_double"), bSuccess);
|
|
|
|
|
SoundFileWriteFramesFloat = (SoundFileWriteFramesFloatFuncPtr)GetSoundFileDllExport(TEXT("sf_writef_float"), bSuccess);
|
|
|
|
|
SoundFileWriteFramesDouble = (SoundFileWriteFramesDoubleFuncPtr)GetSoundFileDllExport(TEXT("sf_writef_double"), bSuccess);
|
|
|
|
|
SoundFileReadSamplesFloat = (SoundFileReadSamplesFloatFuncPtr)GetSoundFileDllExport(TEXT("sf_read_float"), bSuccess);
|
|
|
|
|
SoundFileReadSamplesDouble = (SoundFileReadSamplesDoubleFuncPtr)GetSoundFileDllExport(TEXT("sf_read_double"), bSuccess);
|
|
|
|
|
SoundFileWriteSamplesFloat = (SoundFileWriteSamplesFloatFuncPtr)GetSoundFileDllExport(TEXT("sf_write_float"), bSuccess);
|
|
|
|
|
SoundFileWriteSamplesDouble = (SoundFileWriteSamplesDoubleFuncPtr)GetSoundFileDllExport(TEXT("sf_write_double"), bSuccess);
|
|
|
|
|
|
|
|
|
|
// make sure we're successful
|
|
|
|
|
check(bSuccess);
|
|
|
|
|
return bSuccess;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool FUnrealAudioModule::ShutdownSoundFileLib()
|
|
|
|
|
{
|
|
|
|
|
if (SoundFileDllHandle)
|
|
|
|
|
{
|
|
|
|
|
FPlatformProcess::FreeDllHandle(SoundFileDllHandle);
|
|
|
|
|
SoundFileDllHandle = nullptr;
|
|
|
|
|
SoundFileOpen = nullptr;
|
|
|
|
|
SoundFileOpenVirtual = nullptr;
|
|
|
|
|
SoundFileClose = nullptr;
|
|
|
|
|
SoundFileError = nullptr;
|
|
|
|
|
SoundFileStrError = nullptr;
|
|
|
|
|
SoundFileErrorNumber = nullptr;
|
|
|
|
|
SoundFileCommand = nullptr;
|
|
|
|
|
SoundFileFormatCheck = nullptr;
|
|
|
|
|
SoundFileSeek = nullptr;
|
|
|
|
|
SoundFileGetVersion = nullptr;
|
|
|
|
|
SoundFileReadFramesFloat = nullptr;
|
|
|
|
|
SoundFileReadFramesDouble = nullptr;
|
|
|
|
|
SoundFileWriteFramesFloat = nullptr;
|
|
|
|
|
SoundFileWriteFramesDouble = nullptr;
|
|
|
|
|
SoundFileReadSamplesFloat = nullptr;
|
|
|
|
|
SoundFileReadSamplesDouble = nullptr;
|
|
|
|
|
SoundFileWriteSamplesFloat = nullptr;
|
|
|
|
|
SoundFileWriteSamplesDouble = nullptr;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TSharedPtr<FSoundFileReader> FUnrealAudioModule::CreateSoundFileReader()
|
|
|
|
|
{
|
|
|
|
|
return TSharedPtr<FSoundFileReader>(new FSoundFileReader(this));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TSharedPtr<FSoundFileWriter> FUnrealAudioModule::CreateSoundFileWriter()
|
|
|
|
|
{
|
|
|
|
|
return TSharedPtr<FSoundFileWriter>(new FSoundFileWriter(this));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/************************************************************************/
|
|
|
|
|
/* Exported Functions */
|
|
|
|
|
/************************************************************************/
|
|
|
|
|
|
|
|
|
|
bool GetListOfSoundFilesInDirectory(const FString& Directory, TArray<FString>& OutSoundFileList, FString* TypeFilter /*= nullptr */)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool GetSoundFileDescription(const FString& FilePath, FSoundFileDescription& OutputDescription, TArray<ESoundFileChannelMap::Type>& OutChannelMap)
|
|
|
|
|
{
|
|
|
|
|
LibSoundFileHandle* FileHandle = nullptr;
|
|
|
|
|
ESoundFileError::Type Error = GetSoundDesriptionInternal(&FileHandle, FilePath, OutputDescription, OutChannelMap);
|
|
|
|
|
if (Error == ESoundFileError::NONE)
|
|
|
|
|
{
|
|
|
|
|
DEBUG_AUDIO_CHECK(FileHandle != nullptr);
|
|
|
|
|
SoundFileClose(FileHandle);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool GetSoundFileDescription(const FString& FilePath, FSoundFileDescription& OutputDescription)
|
|
|
|
|
{
|
|
|
|
|
TArray<ESoundFileChannelMap::Type> OutChannelMap;
|
|
|
|
|
return GetSoundFileDescription(FilePath, OutputDescription, OutChannelMap);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool GetFileExtensionForFormatFlags(int32 FormatFlags, FString& OutExtension)
|
|
|
|
|
{
|
|
|
|
|
if (FormatFlags & ESoundFileFormat::OGG)
|
|
|
|
|
{
|
|
|
|
|
OutExtension = TEXT("ogg");
|
|
|
|
|
}
|
|
|
|
|
else if (FormatFlags & ESoundFileFormat::WAV)
|
|
|
|
|
{
|
|
|
|
|
OutExtension = TEXT("wav");
|
|
|
|
|
}
|
|
|
|
|
else if (FormatFlags & ESoundFileFormat::AIFF)
|
|
|
|
|
{
|
|
|
|
|
OutExtension = TEXT("aiff");
|
|
|
|
|
}
|
|
|
|
|
else if (FormatFlags & ESoundFileFormat::FLAC)
|
|
|
|
|
{
|
|
|
|
|
OutExtension = TEXT("flac");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ESoundFileError::Type GetSoundFileInfoFromPath(const FString& FilePath, FSoundFileDescription& Description, TArray<ESoundFileChannelMap::Type>& ChannelMap)
|
|
|
|
|
{
|
|
|
|
|
// Load the description and channel map info
|
|
|
|
|
LibSoundFileHandle* FileHandle = nullptr;
|
|
|
|
|
ESoundFileError::Type Error = GetSoundDesriptionInternal(&FileHandle, FilePath, Description, ChannelMap);
|
|
|
|
|
if (FileHandle)
|
|
|
|
|
{
|
|
|
|
|
SoundFileClose(FileHandle);
|
|
|
|
|
}
|
|
|
|
|
return Error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ESoundFileError::Type LoadSoundFileFromPath(const FString& FilePath, FSoundFileDescription& Description, TArray<ESoundFileChannelMap::Type>& ChannelMap, TArray<uint8>& BulkData)
|
|
|
|
|
{
|
|
|
|
|
ESoundFileError::Type Error = GetSoundFileInfoFromPath(FilePath, Description, ChannelMap);
|
|
|
|
|
if (Error != ESoundFileError::NONE)
|
|
|
|
|
{
|
|
|
|
|
return Error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Now read the data from disk into the bulk data array
|
|
|
|
|
if (FFileHelper::LoadFileToArray(BulkData, *FilePath))
|
|
|
|
|
{
|
|
|
|
|
return ESoundFileError::NONE;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
return ESoundFileError::FAILED_TO_LOAD_BYTE_DATA;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
Copying //UE4/Dev-Build to //UE4/Dev-Main (Source: //UE4/Dev-Build @ 3209340)
#lockdown Nick.Penwarden
#rb none
==========================
MAJOR FEATURES + CHANGES
==========================
Change 3209340 on 2016/11/23 by Ben.Marsh
Convert UE4 codebase to an "include what you use" model - where every header just includes the dependencies it needs, rather than every source file including large monolithic headers like Engine.h and UnrealEd.h.
Measured full rebuild times around 2x faster using XGE on Windows, and improvements of 25% or more for incremental builds and full rebuilds on most other platforms.
* Every header now includes everything it needs to compile.
* There's a CoreMinimal.h header that gets you a set of ubiquitous types from Core (eg. FString, FName, TArray, FVector, etc...). Most headers now include this first.
* There's a CoreTypes.h header that sets up primitive UE4 types and build macros (int32, PLATFORM_WIN64, etc...). All headers in Core include this first, as does CoreMinimal.h.
* Every .cpp file includes its matching .h file first.
* This helps validate that each header is including everything it needs to compile.
* No engine code includes a monolithic header such as Engine.h or UnrealEd.h any more.
* You will get a warning if you try to include one of these from the engine. They still exist for compatibility with game projects and do not produce warnings when included there.
* There have only been minor changes to our internal games down to accommodate these changes. The intent is for this to be as seamless as possible.
* No engine code explicitly includes a precompiled header any more.
* We still use PCHs, but they're force-included on the compiler command line by UnrealBuildTool instead. This lets us tune what they contain without breaking any existing include dependencies.
* PCHs are generated by a tool to get a statistical amount of coverage for the source files using it, and I've seeded the new shared PCHs to contain any header included by > 15% of source files.
Tool used to generate this transform is at Engine\Source\Programs\IncludeTool.
[CL 3209342 by Ben Marsh in Main branch]
2016-11-23 15:48:37 -05:00
|
|
|
#endif // #if ENABLE_UNREAL_AUDIO
|