You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
- The entire editor can now be compiled using Clang on Windows. It also runs (as long as you use the MSVC linker.) - Use UEBuildWindows.bCompileWithClang=true to help test Clang support - Most UE4 programs can now be compiled using Clang on Windows, and linked using the Clang linker on Windows - Many C++ syntax fixes to resolve Clang errors and warnings when on Windows - Clang on Windows now supports PCH files properly (but not "shared" PCHs, yet.) - Hacked the DirectX XAudio2 headers slightly to work around a macro pasting bug in Clang [CL 2494439 by Mike Fricker in Main branch]
643 lines
19 KiB
C++
643 lines
19 KiB
C++
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
|
|
|
|
/*=============================================================================
|
|
XeAudioDevice.cpp: Unreal XAudio2 Audio interface object.
|
|
|
|
Unreal is RHS with Y and Z swapped (or technically LHS with flipped axis)
|
|
|
|
=============================================================================*/
|
|
|
|
/*------------------------------------------------------------------------------------
|
|
Audio includes.
|
|
------------------------------------------------------------------------------------*/
|
|
|
|
#include "XAudio2Device.h"
|
|
#include "AudioEffect.h"
|
|
#include "OpusAudioInfo.h"
|
|
#include "VorbisAudioInfo.h"
|
|
#include "XAudio2Effects.h"
|
|
#include "Engine.h"
|
|
#include "AllowWindowsPlatformTypes.h"
|
|
#include <xapobase.h>
|
|
#include <xapofx.h>
|
|
#include <xaudio2fx.h>
|
|
#include "HideWindowsPlatformTypes.h"
|
|
#include "TargetPlatform.h"
|
|
#include "XAudio2Support.h"
|
|
|
|
DEFINE_LOG_CATEGORY(LogXAudio2);
|
|
|
|
class FXAudio2DeviceModule : public IAudioDeviceModule
|
|
{
|
|
public:
|
|
|
|
/** Creates a new instance of the audio device implemented by the module. */
|
|
virtual FAudioDevice* CreateAudioDevice() override
|
|
{
|
|
return new FXAudio2Device;
|
|
}
|
|
};
|
|
|
|
IMPLEMENT_MODULE(FXAudio2DeviceModule, XAudio2);
|
|
|
|
/*------------------------------------------------------------------------------------
|
|
Static variables from the early init
|
|
------------------------------------------------------------------------------------*/
|
|
|
|
// The number of speakers producing sound (stereo or 5.1)
|
|
int32 FXAudioDeviceProperties::NumSpeakers = 0;
|
|
const float* FXAudioDeviceProperties::OutputMixMatrix = NULL;
|
|
#if XAUDIO_SUPPORTS_DEVICE_DETAILS
|
|
XAUDIO2_DEVICE_DETAILS FXAudioDeviceProperties::DeviceDetails;
|
|
#endif //XAUDIO_SUPPORTS_DEVICE_DETAILS
|
|
|
|
/*------------------------------------------------------------------------------------
|
|
FAudioDevice Interface.
|
|
------------------------------------------------------------------------------------*/
|
|
|
|
#define DEBUG_XAUDIO2 0
|
|
|
|
FSpatializationHelper FXAudio2Device::SpatializationHelper;
|
|
|
|
bool FXAudio2Device::InitializeHardware()
|
|
{
|
|
if (IsRunningDedicatedServer())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Create a new DeviceProperties object
|
|
DeviceProperties = new FXAudioDeviceProperties;
|
|
// null out the non-static data
|
|
DeviceProperties->XAudio2 = nullptr;
|
|
DeviceProperties->MasteringVoice = nullptr;
|
|
|
|
// Load ogg and vorbis dlls if they haven't been loaded yet
|
|
LoadVorbisLibraries();
|
|
|
|
SampleRate = 0;
|
|
|
|
#if PLATFORM_WINDOWS
|
|
bComInitialized = FWindowsPlatformMisc::CoInitialize();
|
|
#if PLATFORM_64BITS
|
|
// Work around the fact the x64 version of XAudio2_7.dll does not properly ref count
|
|
// by forcing it to be always loaded
|
|
|
|
// Load the xaudio2 library and keep a handle so we can free it on teardown
|
|
// Note: windows internally ref-counts the library per call to load library so
|
|
// when we call FreeLibrary, it will only free it once the refcount is zero
|
|
DeviceProperties->XAudio2Dll = LoadLibraryA("XAudio2_7.dll");
|
|
|
|
// returning null means we failed to load XAudio2, which means everything will fail
|
|
if (DeviceProperties->XAudio2Dll == nullptr)
|
|
{
|
|
UE_LOG(LogInit, Warning, TEXT("Failed to load XAudio2 dll"));
|
|
return false;
|
|
}
|
|
#endif //PLATFORM_64BITS
|
|
#endif //PLATFORM_WINDOWS
|
|
|
|
#if DEBUG_XAUDIO2
|
|
uint32 Flags = XAUDIO2_DEBUG_ENGINE;
|
|
#else
|
|
uint32 Flags = 0;
|
|
#endif
|
|
|
|
// Create a new XAudio2 device object instance
|
|
if (XAudio2Create(&DeviceProperties->XAudio2, Flags, AUDIO_HWTHREAD) != S_OK)
|
|
{
|
|
UE_LOG(LogInit, Log, TEXT( "Failed to create XAudio2 interface" ) );
|
|
return( false );
|
|
}
|
|
|
|
check(DeviceProperties->XAudio2 != nullptr);
|
|
|
|
#if XAUDIO_SUPPORTS_DEVICE_DETAILS
|
|
UINT32 DeviceCount = 0;
|
|
ValidateAPICall(TEXT("GetDeviceCount"),
|
|
DeviceProperties->XAudio2->GetDeviceCount(&DeviceCount));
|
|
if( DeviceCount < 1 )
|
|
{
|
|
UE_LOG(LogInit, Log, TEXT( "No audio devices found!" ) );
|
|
DeviceProperties->XAudio2 = nullptr;
|
|
return( false );
|
|
}
|
|
|
|
// Get the details of the default device 0
|
|
if( !ValidateAPICall(TEXT("GetDeviceDetails"),
|
|
DeviceProperties->XAudio2->GetDeviceDetails(0, &FXAudioDeviceProperties::DeviceDetails)))
|
|
{
|
|
UE_LOG(LogInit, Log, TEXT( "Failed to get DeviceDetails for XAudio2" ) );
|
|
DeviceProperties->XAudio2 = nullptr;
|
|
return( false );
|
|
}
|
|
|
|
#if DEBUG_XAUDIO2
|
|
XAUDIO2_DEBUG_CONFIGURATION DebugConfig = {0};
|
|
DebugConfig.TraceMask = XAUDIO2_LOG_WARNINGS | XAUDIO2_LOG_DETAIL;
|
|
DebugConfig.BreakMask = XAUDIO2_LOG_ERRORS;
|
|
DeviceProperties->XAudio2->SetDebugConfiguration(&DebugConfig);
|
|
#endif
|
|
|
|
FXAudioDeviceProperties::NumSpeakers = UE4_XAUDIO2_NUMCHANNELS;
|
|
SampleRate = FXAudioDeviceProperties::DeviceDetails.OutputFormat.Format.nSamplesPerSec;
|
|
|
|
// Clamp the output frequency to the limits of the reverb XAPO
|
|
if( SampleRate > XAUDIO2FX_REVERB_MAX_FRAMERATE )
|
|
{
|
|
SampleRate = XAUDIO2FX_REVERB_MAX_FRAMERATE;
|
|
FXAudioDeviceProperties::DeviceDetails.OutputFormat.Format.nSamplesPerSec = SampleRate;
|
|
}
|
|
|
|
UE_LOG(LogInit, Log, TEXT( "XAudio2 using '%s' : %d channels at %g kHz using %d bits per sample (channel mask 0x%x)" ),
|
|
FXAudioDeviceProperties::DeviceDetails.DisplayName,
|
|
FXAudioDeviceProperties::NumSpeakers,
|
|
( float )SampleRate / 1000.0f,
|
|
FXAudioDeviceProperties::DeviceDetails.OutputFormat.Format.wBitsPerSample,
|
|
(uint32)UE4_XAUDIO2_CHANNELMASK );
|
|
|
|
if( !GetOutputMatrix( UE4_XAUDIO2_CHANNELMASK, FXAudioDeviceProperties::NumSpeakers ) )
|
|
{
|
|
UE_LOG(LogInit, Log, TEXT( "Unsupported speaker configuration for this number of channels" ) );
|
|
DeviceProperties->XAudio2 = NULL;
|
|
return( false );
|
|
}
|
|
|
|
// Create the final output voice with either 2 or 6 channels
|
|
if (!ValidateAPICall(TEXT("CreateMasteringVoice"),
|
|
DeviceProperties->XAudio2->CreateMasteringVoice(&DeviceProperties->MasteringVoice, FXAudioDeviceProperties::NumSpeakers, SampleRate, 0, 0, NULL)))
|
|
{
|
|
UE_LOG(LogInit, Warning, TEXT( "Failed to create the mastering voice for XAudio2" ) );
|
|
DeviceProperties->XAudio2 = nullptr;
|
|
return( false );
|
|
}
|
|
#else //XAUDIO_SUPPORTS_DEVICE_DETAILS
|
|
// Create the final output voice
|
|
if (!ValidateAPICall(TEXT("CreateMasteringVoice"),
|
|
DeviceProperties->XAudio2->CreateMasteringVoice(&DeviceProperties->MasteringVoice, UE4_XAUDIO2_NUMCHANNELS, UE4_XAUDIO2_SAMPLERATE, 0, 0, NULL )))
|
|
{
|
|
UE_LOG(LogInit, Warning, TEXT( "Failed to create the mastering voice for XAudio2" ) );
|
|
DeviceProperties->XAudio2 = nullptr;
|
|
return false;
|
|
}
|
|
#endif //XAUDIO_SUPPORTS_DEVICE_DETAILS
|
|
|
|
SpatializationHelper.Init();
|
|
|
|
// Initialize permanent memory stack for initial & always loaded sound allocations.
|
|
if( CommonAudioPoolSize )
|
|
{
|
|
UE_LOG(LogAudio, Log, TEXT( "Allocating %g MByte for always resident audio data" ), CommonAudioPoolSize / ( 1024.0f * 1024.0f ) );
|
|
CommonAudioPoolFreeBytes = CommonAudioPoolSize;
|
|
CommonAudioPool = ( uint8* )FMemory::Malloc( CommonAudioPoolSize );
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAudio, Log, TEXT( "CommonAudioPoolSize is set to 0 - disabling persistent pool for audio data" ) );
|
|
CommonAudioPoolFreeBytes = 0;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void FXAudio2Device::TeardownHardware()
|
|
{
|
|
if (DeviceProperties)
|
|
{
|
|
// close hardware interfaces
|
|
if (DeviceProperties->MasteringVoice)
|
|
{
|
|
DeviceProperties->MasteringVoice->DestroyVoice();
|
|
DeviceProperties->MasteringVoice = NULL;
|
|
}
|
|
|
|
if (DeviceProperties->XAudio2)
|
|
{
|
|
// Force the hardware to release all references
|
|
DeviceProperties->XAudio2->Release();
|
|
DeviceProperties->XAudio2 = NULL;
|
|
}
|
|
|
|
#if PLATFORM_WINDOWS && PLATFORM_64BITS
|
|
if (DeviceProperties->XAudio2Dll)
|
|
{
|
|
if (!FreeLibrary(DeviceProperties->XAudio2Dll))
|
|
{
|
|
UE_LOG(LogAudio, Warning, TEXT("Failed to free XAudio2 Dll"));
|
|
}
|
|
}
|
|
#endif
|
|
|
|
delete DeviceProperties;
|
|
DeviceProperties = nullptr;
|
|
}
|
|
|
|
#if PLATFORM_WINDOWS
|
|
if (bComInitialized)
|
|
{
|
|
FWindowsPlatformMisc::CoUninitialize();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void FXAudio2Device::UpdateHardware()
|
|
{
|
|
if (Listeners.Num() > 0)
|
|
{
|
|
// Caches the matrix used to transform a sounds position into local space so we can just look
|
|
// at the Y component after normalization to determine spatialization.
|
|
const FVector Up = Listeners[0].GetUp();
|
|
const FVector Right = Listeners[0].GetFront();
|
|
InverseTransform = FMatrix(Up, Right, Up ^ Right, Listeners[0].Transform.GetTranslation()).InverseFast();
|
|
}
|
|
}
|
|
|
|
FAudioEffectsManager* FXAudio2Device::CreateEffectsManager()
|
|
{
|
|
// Create the effects subsystem (reverb, EQ, etc.)
|
|
return new FXAudio2EffectsManager(this);
|
|
}
|
|
|
|
FSoundSource* FXAudio2Device::CreateSoundSource()
|
|
{
|
|
// create source source object
|
|
return new FXAudio2SoundSource(this);
|
|
}
|
|
|
|
bool FXAudio2Device::HasCompressedAudioInfoClass(USoundWave* SoundWave)
|
|
{
|
|
#if WITH_OGGVORBIS
|
|
return true;
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
class ICompressedAudioInfo* FXAudio2Device::CreateCompressedAudioInfo(USoundWave* SoundWave)
|
|
{
|
|
if (SoundWave->IsStreaming())
|
|
{
|
|
return new FOpusAudioInfo();
|
|
}
|
|
|
|
#if WITH_OGGVORBIS
|
|
if (SoundWave->CompressionName.IsNone() || SoundWave->CompressionName == TEXT("OGG"))
|
|
{
|
|
|
|
return new FVorbisAudioInfo();
|
|
}
|
|
else
|
|
{
|
|
return NULL;
|
|
}
|
|
#else
|
|
return NULL;
|
|
#endif
|
|
|
|
}
|
|
|
|
/**
|
|
* Check for errors and output a human readable string
|
|
*/
|
|
bool FXAudio2Device::ValidateAPICall( const TCHAR* Function, uint32 ErrorCode )
|
|
{
|
|
if( ErrorCode != S_OK )
|
|
{
|
|
switch( ErrorCode )
|
|
{
|
|
case XAUDIO2_E_INVALID_CALL:
|
|
UE_LOG(LogAudio, Warning, TEXT( "%s error: Invalid Call" ), Function );
|
|
break;
|
|
|
|
case XAUDIO2_E_XMA_DECODER_ERROR:
|
|
UE_LOG(LogAudio, Warning, TEXT( "%s error: XMA Decoder Error" ), Function );
|
|
break;
|
|
|
|
case XAUDIO2_E_XAPO_CREATION_FAILED:
|
|
UE_LOG(LogAudio, Warning, TEXT( "%s error: XAPO Creation Failed" ), Function );
|
|
break;
|
|
|
|
case XAUDIO2_E_DEVICE_INVALIDATED:
|
|
UE_LOG(LogAudio, Warning, TEXT( "%s error: Device Invalidated" ), Function );
|
|
break;
|
|
|
|
default:
|
|
UE_LOG(LogAudio, Warning, TEXT( "%s error: Unhandled error code %d" ), Function, ErrorCode );
|
|
break;
|
|
};
|
|
|
|
return( false );
|
|
}
|
|
|
|
return( true );
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
* Derives the output matrix to use based on the channel mask and the number of channels
|
|
*/
|
|
const float OutputMatrixMono[SPEAKER_COUNT] =
|
|
{
|
|
0.7f, 0.7f, 0.5f, 0.0f, 0.5f, 0.5f
|
|
};
|
|
|
|
const float OutputMatrix2dot0[SPEAKER_COUNT * 2] =
|
|
{
|
|
1.0f, 0.0f, 0.7f, 0.0f, 1.25f, 0.0f, // FL
|
|
0.0f, 1.0f, 0.7f, 0.0f, 0.0f, 1.25f // FR
|
|
};
|
|
|
|
// const float OutputMatrixDownMix[SPEAKER_COUNT * 2] =
|
|
// {
|
|
// 1.0f, 0.0f, 0.7f, 0.0f, 0.87f, 0.49f,
|
|
// 0.0f, 1.0f, 0.7f, 0.0f, 0.49f, 0.87f
|
|
// };
|
|
|
|
const float OutputMatrix2dot1[SPEAKER_COUNT * 3] =
|
|
{
|
|
// Same as stereo, but also passing in LFE signal
|
|
1.0f, 0.0f, 0.7f, 0.0f, 1.25f, 0.0f, // FL
|
|
0.0f, 1.0f, 0.7f, 0.0f, 0.0f, 1.25f, // FR
|
|
0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // LFE
|
|
};
|
|
|
|
const float OutputMatrix4dot0s[SPEAKER_COUNT * 4] =
|
|
{
|
|
// Combine both rear channels to make a rear center channel
|
|
1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, // FL
|
|
0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, // FR
|
|
0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, // FC
|
|
0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f // RC
|
|
};
|
|
|
|
const float OutputMatrix4dot0[SPEAKER_COUNT * 4] =
|
|
{
|
|
// Split the center channel to the front two speakers
|
|
1.0f, 0.0f, 0.7f, 0.0f, 0.0f, 0.0f, // FL
|
|
0.0f, 1.0f, 0.7f, 0.0f, 0.0f, 0.0f, // FR
|
|
0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, // RL
|
|
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f // RR
|
|
};
|
|
|
|
const float OutputMatrix4dot1[SPEAKER_COUNT * 5] =
|
|
{
|
|
// Split the center channel to the front two speakers
|
|
1.0f, 0.0f, 0.7f, 0.0f, 0.0f, 0.0f, // FL
|
|
0.0f, 1.0f, 0.7f, 0.0f, 0.0f, 0.0f, // FR
|
|
0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // LFE
|
|
0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, // RL
|
|
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f // RR
|
|
};
|
|
|
|
const float OutputMatrix5dot0[SPEAKER_COUNT * 5] =
|
|
{
|
|
// Split the center channel to the front two speakers
|
|
1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, // FL
|
|
0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, // FR
|
|
0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, // FC
|
|
0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, // SL
|
|
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f // SR
|
|
};
|
|
|
|
const float OutputMatrix5dot1[SPEAKER_COUNT * 6] =
|
|
{
|
|
// Classic 5.1 setup
|
|
1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, // FL
|
|
0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, // FR
|
|
0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, // FC
|
|
0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // LFE
|
|
0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, // RL
|
|
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f // RR
|
|
};
|
|
|
|
const float OutputMatrix5dot1s[SPEAKER_COUNT * 6] =
|
|
{
|
|
// Classic 5.1 setup
|
|
1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, // FL
|
|
0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, // FR
|
|
0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, // FC
|
|
0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // LFE
|
|
0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, // SL
|
|
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f // SR
|
|
};
|
|
|
|
const float OutputMatrix6dot1[SPEAKER_COUNT * 7] =
|
|
{
|
|
1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, // FL
|
|
0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, // FR
|
|
0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, // FC
|
|
0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // LFE
|
|
0.0f, 0.0f, 0.0f, 0.0f, 0.8f, 0.0f, // RL
|
|
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.8f, // RR
|
|
0.0f, 0.0f, 0.0f, 0.0f, 0.5f, 0.5f, // RC
|
|
};
|
|
|
|
const float OutputMatrix7dot1[SPEAKER_COUNT * 8] =
|
|
{
|
|
0.7f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, // FL
|
|
0.0f, 0.7f, 0.0f, 0.0f, 0.0f, 0.0f, // FR
|
|
0.0f, 0.0f, 0.7f, 0.0f, 0.0f, 0.0f, // FC
|
|
0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // LFE
|
|
0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, // RL
|
|
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, // RR
|
|
0.7f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f, // FCL
|
|
0.0f, 0.7f, 0.5f, 0.0f, 0.0f, 0.0f // FCR
|
|
};
|
|
|
|
const float OutputMatrix7dot1s[SPEAKER_COUNT * 8] =
|
|
{
|
|
// Split the rear channels evenly between side and rear
|
|
1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, // FL
|
|
0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, // FR
|
|
0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, // FC
|
|
0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // LFE
|
|
0.0f, 0.0f, 0.0f, 0.0f, 0.7f, 0.0f, // RL
|
|
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.7f, // RR
|
|
0.0f, 0.0f, 0.0f, 0.0f, 0.7f, 0.0f, // SL
|
|
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.7f // SR
|
|
};
|
|
|
|
typedef struct SOuputMapping
|
|
{
|
|
uint32 NumChannels;
|
|
uint32 SpeakerMask;
|
|
const float* OuputMatrix;
|
|
} TOuputMapping;
|
|
|
|
TOuputMapping OutputMappings[] =
|
|
{
|
|
{ 1, SPEAKER_MONO, OutputMatrixMono },
|
|
{ 2, SPEAKER_STEREO, OutputMatrix2dot0 },
|
|
{ 3, SPEAKER_2POINT1, OutputMatrix2dot1 },
|
|
{ 4, SPEAKER_SURROUND, OutputMatrix4dot0s },
|
|
{ 4, SPEAKER_QUAD, OutputMatrix4dot0 },
|
|
{ 5, SPEAKER_4POINT1, OutputMatrix4dot1 },
|
|
{ 5, SPEAKER_5POINT0, OutputMatrix5dot0 },
|
|
{ 6, SPEAKER_5POINT1, OutputMatrix5dot1 },
|
|
{ 6, SPEAKER_5POINT1_SURROUND, OutputMatrix5dot1s },
|
|
{ 7, SPEAKER_6POINT1, OutputMatrix6dot1 },
|
|
{ 8, SPEAKER_7POINT1, OutputMatrix7dot1 },
|
|
{ 8, SPEAKER_7POINT1_SURROUND, OutputMatrix7dot1s }
|
|
};
|
|
|
|
bool FXAudio2Device::GetOutputMatrix( uint32 ChannelMask, uint32 NumChannels )
|
|
{
|
|
// Default is vanilla stereo
|
|
FXAudioDeviceProperties::OutputMixMatrix = OutputMatrix2dot0;
|
|
|
|
// Find the best match
|
|
for( int32 MappingIndex = 0; MappingIndex < sizeof( OutputMappings ) / sizeof( TOuputMapping ); MappingIndex++ )
|
|
{
|
|
if( NumChannels != OutputMappings[MappingIndex].NumChannels )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if( ( ChannelMask & OutputMappings[MappingIndex].SpeakerMask ) != ChannelMask )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
FXAudioDeviceProperties::OutputMixMatrix = OutputMappings[MappingIndex].OuputMatrix;
|
|
break;
|
|
}
|
|
|
|
return( FXAudioDeviceProperties::OutputMixMatrix != NULL );
|
|
}
|
|
|
|
|
|
/** Test decompress a vorbis file */
|
|
void FXAudio2Device::TestDecompressOggVorbis( USoundWave* Wave )
|
|
{
|
|
FVorbisAudioInfo OggInfo;
|
|
FSoundQualityInfo QualityInfo = { 0 };
|
|
|
|
// Parse the ogg vorbis header for the relevant information
|
|
if( OggInfo.ReadCompressedInfo( Wave->ResourceData, Wave->ResourceSize, &QualityInfo ) )
|
|
{
|
|
// Extract the data
|
|
Wave->SampleRate = QualityInfo.SampleRate;
|
|
Wave->NumChannels = QualityInfo.NumChannels;
|
|
Wave->RawPCMDataSize = QualityInfo.SampleDataSize;
|
|
Wave->Duration = QualityInfo.Duration;
|
|
|
|
Wave->RawPCMData = ( uint8* )FMemory::Malloc( Wave->RawPCMDataSize );
|
|
|
|
// Decompress all the sample data (and preallocate memory)
|
|
OggInfo.ExpandFile( Wave->RawPCMData, &QualityInfo );
|
|
|
|
FMemory::Free( Wave->RawPCMData );
|
|
}
|
|
}
|
|
|
|
#if !UE_BUILD_SHIPPING
|
|
/** Decompress a wav a number of times for profiling purposes */
|
|
void FXAudio2Device::TimeTest( FOutputDevice& Ar, const TCHAR* WaveAssetName )
|
|
{
|
|
USoundWave* Wave = LoadObject<USoundWave>( NULL, WaveAssetName, NULL, LOAD_NoWarn, NULL );
|
|
if( Wave )
|
|
{
|
|
// Wait for initial decompress
|
|
if (Wave->AudioDecompressor)
|
|
{
|
|
while (!Wave->AudioDecompressor->IsDone())
|
|
{
|
|
}
|
|
|
|
delete Wave->AudioDecompressor;
|
|
Wave->AudioDecompressor = NULL;
|
|
}
|
|
|
|
// If the wave loaded in fine, time the decompression
|
|
Wave->InitAudioResource(GetRuntimeFormat(Wave));
|
|
|
|
double Start = FPlatformTime::Seconds();
|
|
|
|
for( int32 i = 0; i < 1000; i++ )
|
|
{
|
|
TestDecompressOggVorbis( Wave );
|
|
}
|
|
|
|
double Duration = FPlatformTime::Seconds() - Start;
|
|
Ar.Logf( TEXT( "%s: %g ms - %g ms per second per channel" ), WaveAssetName, Duration, Duration / ( Wave->Duration * Wave->NumChannels ) );
|
|
|
|
Wave->RemoveAudioResource();
|
|
}
|
|
else
|
|
{
|
|
Ar.Logf( TEXT( "Failed to find test file '%s' to decompress" ), WaveAssetName );
|
|
}
|
|
}
|
|
#endif // !UE_BUILD_SHIPPING
|
|
|
|
/**
|
|
* Exec handler used to parse console commands.
|
|
*
|
|
* @param InWorld World context
|
|
* @param Cmd Command to parse
|
|
* @param Ar Output device to use in case the handler prints anything
|
|
* @return true if command was handled, false otherwise
|
|
*/
|
|
bool FXAudio2Device::Exec( UWorld* InWorld, const TCHAR* Cmd, FOutputDevice& Ar )
|
|
{
|
|
if( FAudioDevice::Exec( InWorld, Cmd, Ar ) )
|
|
{
|
|
return( true );
|
|
}
|
|
#if !UE_BUILD_SHIPPING
|
|
else if( FParse::Command( &Cmd, TEXT( "TestVorbisDecompressionSpeed" ) ) )
|
|
{
|
|
|
|
TimeTest( Ar, TEXT( "TestSounds.44Mono_TestWeaponSynthetic" ) );
|
|
TimeTest( Ar, TEXT( "TestSounds.44Mono_TestDialogFemale" ) );
|
|
TimeTest( Ar, TEXT( "TestSounds.44Mono_TestDialogMale" ) );
|
|
|
|
TimeTest( Ar, TEXT( "TestSounds.22Mono_TestWeaponSynthetic" ) );
|
|
TimeTest( Ar, TEXT( "TestSounds.22Mono_TestDialogFemale" ) );
|
|
TimeTest( Ar, TEXT( "TestSounds.22Mono_TestDialogMale" ) );
|
|
|
|
TimeTest( Ar, TEXT( "TestSounds.22Stereo_TestMusicAcoustic" ) );
|
|
TimeTest( Ar, TEXT( "TestSounds.44Stereo_TestMusicAcoustic" ) );
|
|
}
|
|
#endif // !UE_BUILD_SHIPPING
|
|
|
|
return( false );
|
|
}
|
|
|
|
/**
|
|
* Allocates memory from permanent pool. This memory will NEVER be freed.
|
|
*
|
|
* @param Size Size of allocation.
|
|
*
|
|
* @return pointer to a chunk of memory with size Size
|
|
*/
|
|
void* FXAudio2Device::AllocatePermanentMemory( int32 Size, bool& AllocatedInPool )
|
|
{
|
|
void* Allocation = NULL;
|
|
|
|
// Fall back to using regular allocator if there is not enough space in permanent memory pool.
|
|
if( Size > CommonAudioPoolFreeBytes )
|
|
{
|
|
Allocation = FMemory::Malloc( Size );
|
|
check( Allocation );
|
|
|
|
AllocatedInPool = false;
|
|
}
|
|
// Allocate memory from pool.
|
|
else
|
|
{
|
|
uint8* CommonAudioPoolAddress = ( uint8* )CommonAudioPool;
|
|
Allocation = CommonAudioPoolAddress + ( CommonAudioPoolSize - CommonAudioPoolFreeBytes );
|
|
|
|
AllocatedInPool = true;
|
|
}
|
|
|
|
// Decrement available size regardless of whether we allocated from pool or used regular allocator
|
|
// to allow us to log suggested size at the end of initial loading.
|
|
CommonAudioPoolFreeBytes -= Size;
|
|
|
|
return( Allocation );
|
|
}
|