Files
UnrealEngineUWP/Engine/Source/Runtime/UnrealAudio/Private/UnrealAudioSoundFileReader.cpp

1042 lines
34 KiB
C++
Raw Normal View History

// Copyright Epic Games, Inc. All Rights Reserved.
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"
Unreal Audio WIP * Restructured sound file reading/writing classes - 2 classes: a sound file reader and a sound file writer - sound file reader decodes sound file data for supported formats, can read stream directly off disk - sound file writer writes to a bulk data buffer, can write for supported formats * First pass sound file asset manager - has target memory usage for loaded sound files - flushes unrefed sound files when usage is above threshold - flushes sound files that haven't been used above a time threshold (e.g. 30 seconds) - supports sync and async loading from disk - supports loads from bulk data - some tests which simulate randomized real-game load patterns. - rewrote tests for batch asset conversion (from X to Y audio formats, including sample rates, see below) * Implemented a flexible linear-based sample rate converter - Can be used for real-time decoding and for sample format conversion * First pass voice manager - supports real and virtual voices and logic to steal real voices based on volume-weighted priority - organized to attempt to maximize cache coherency - manages messages from main-thread to audio-system thread to update voice params * Sketches on low level mixer, not yet fully implemented - first pass implementation on async sound file decoding for "real" voices * First pass on thread message system between main/audio threads and audio thread to device thread. - plan to switch to "named thread" system used by rendering thread for main->audio system thread communication * First pass on emitter class - listener class not implemented, this is testing creating emitters and passing emitter params to audio system thread * Couple utility classes useful for thread safe message passing - simple lockless single producer/single consumer dynamic queue for message passing - multi-producer/multi-consumer dynamic queue for message passing (not used) - simple class to check for thread ids to ensure functions are called on correct threads [CL 2620445 by Aaron McLeran in Main branch]
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"
Unreal Audio WIP * Restructured sound file reading/writing classes - 2 classes: a sound file reader and a sound file writer - sound file reader decodes sound file data for supported formats, can read stream directly off disk - sound file writer writes to a bulk data buffer, can write for supported formats * First pass sound file asset manager - has target memory usage for loaded sound files - flushes unrefed sound files when usage is above threshold - flushes sound files that haven't been used above a time threshold (e.g. 30 seconds) - supports sync and async loading from disk - supports loads from bulk data - some tests which simulate randomized real-game load patterns. - rewrote tests for batch asset conversion (from X to Y audio formats, including sample rates, see below) * Implemented a flexible linear-based sample rate converter - Can be used for real-time decoding and for sample format conversion * First pass voice manager - supports real and virtual voices and logic to steal real voices based on volume-weighted priority - organized to attempt to maximize cache coherency - manages messages from main-thread to audio-system thread to update voice params * Sketches on low level mixer, not yet fully implemented - first pass implementation on async sound file decoding for "real" voices * First pass on thread message system between main/audio threads and audio thread to device thread. - plan to switch to "named thread" system used by rendering thread for main->audio system thread communication * First pass on emitter class - listener class not implemented, this is testing creating emitters and passing emitter params to audio system thread * Couple utility classes useful for thread safe message passing - simple lockless single producer/single consumer dynamic queue for message passing - multi-producer/multi-consumer dynamic queue for message passing (not used) - simple class to check for thread ids to ensure functions are called on correct threads [CL 2620445 by Aaron McLeran in Main branch]
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);
Unreal Audio WIP * Restructured sound file reading/writing classes - 2 classes: a sound file reader and a sound file writer - sound file reader decodes sound file data for supported formats, can read stream directly off disk - sound file writer writes to a bulk data buffer, can write for supported formats * First pass sound file asset manager - has target memory usage for loaded sound files - flushes unrefed sound files when usage is above threshold - flushes sound files that haven't been used above a time threshold (e.g. 30 seconds) - supports sync and async loading from disk - supports loads from bulk data - some tests which simulate randomized real-game load patterns. - rewrote tests for batch asset conversion (from X to Y audio formats, including sample rates, see below) * Implemented a flexible linear-based sample rate converter - Can be used for real-time decoding and for sample format conversion * First pass voice manager - supports real and virtual voices and logic to steal real voices based on volume-weighted priority - organized to attempt to maximize cache coherency - manages messages from main-thread to audio-system thread to update voice params * Sketches on low level mixer, not yet fully implemented - first pass implementation on async sound file decoding for "real" voices * First pass on thread message system between main/audio threads and audio thread to device thread. - plan to switch to "named thread" system used by rendering thread for main->audio system thread communication * First pass on emitter class - listener class not implemented, this is testing creating emitters and passing emitter params to audio system thread * Couple utility classes useful for thread safe message passing - simple lockless single producer/single consumer dynamic queue for message passing - multi-producer/multi-consumer dynamic queue for message passing (not used) - simple class to check for thread ids to ensure functions are called on correct threads [CL 2620445 by Aaron McLeran in Main branch]
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);
Unreal Audio WIP * Restructured sound file reading/writing classes - 2 classes: a sound file reader and a sound file writer - sound file reader decodes sound file data for supported formats, can read stream directly off disk - sound file writer writes to a bulk data buffer, can write for supported formats * First pass sound file asset manager - has target memory usage for loaded sound files - flushes unrefed sound files when usage is above threshold - flushes sound files that haven't been used above a time threshold (e.g. 30 seconds) - supports sync and async loading from disk - supports loads from bulk data - some tests which simulate randomized real-game load patterns. - rewrote tests for batch asset conversion (from X to Y audio formats, including sample rates, see below) * Implemented a flexible linear-based sample rate converter - Can be used for real-time decoding and for sample format conversion * First pass voice manager - supports real and virtual voices and logic to steal real voices based on volume-weighted priority - organized to attempt to maximize cache coherency - manages messages from main-thread to audio-system thread to update voice params * Sketches on low level mixer, not yet fully implemented - first pass implementation on async sound file decoding for "real" voices * First pass on thread message system between main/audio threads and audio thread to device thread. - plan to switch to "named thread" system used by rendering thread for main->audio system thread communication * First pass on emitter class - listener class not implemented, this is testing creating emitters and passing emitter params to audio system thread * Couple utility classes useful for thread safe message passing - simple lockless single producer/single consumer dynamic queue for message passing - multi-producer/multi-consumer dynamic queue for message passing (not used) - simple class to check for thread ids to ensure functions are called on correct threads [CL 2620445 by Aaron McLeran in Main branch]
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)
{
int32 Result2 = SoundFileCommand(FileHandle, SET_ENCODING_QUALITY, &EncodingQuality, sizeof(double));
if (Result2 != 1)
Unreal Audio WIP * Restructured sound file reading/writing classes - 2 classes: a sound file reader and a sound file writer - sound file reader decodes sound file data for supported formats, can read stream directly off disk - sound file writer writes to a bulk data buffer, can write for supported formats * First pass sound file asset manager - has target memory usage for loaded sound files - flushes unrefed sound files when usage is above threshold - flushes sound files that haven't been used above a time threshold (e.g. 30 seconds) - supports sync and async loading from disk - supports loads from bulk data - some tests which simulate randomized real-game load patterns. - rewrote tests for batch asset conversion (from X to Y audio formats, including sample rates, see below) * Implemented a flexible linear-based sample rate converter - Can be used for real-time decoding and for sample format conversion * First pass voice manager - supports real and virtual voices and logic to steal real voices based on volume-weighted priority - organized to attempt to maximize cache coherency - manages messages from main-thread to audio-system thread to update voice params * Sketches on low level mixer, not yet fully implemented - first pass implementation on async sound file decoding for "real" voices * First pass on thread message system between main/audio threads and audio thread to device thread. - plan to switch to "named thread" system used by rendering thread for main->audio system thread communication * First pass on emitter class - listener class not implemented, this is testing creating emitters and passing emitter params to audio system thread * Couple utility classes useful for thread safe message passing - simple lockless single producer/single consumer dynamic queue for message passing - multi-producer/multi-consumer dynamic queue for message passing (not used) - simple class to check for thread ids to ensure functions are called on correct threads [CL 2620445 by Aaron McLeran in Main branch]
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