2014-12-07 19:09:38 -05:00
|
|
|
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
|
2014-03-14 14:13:41 -04:00
|
|
|
|
|
|
|
|
/*=============================================================================
|
|
|
|
|
ALAudioDevice.cpp: Unreal OpenAL Audio interface object.
|
|
|
|
|
|
|
|
|
|
Unreal is RHS with Y and Z swapped (or technically LHS with flipped axis)
|
|
|
|
|
=============================================================================*/
|
|
|
|
|
|
|
|
|
|
/*------------------------------------------------------------------------------------
|
|
|
|
|
Audio includes.
|
|
|
|
|
------------------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
#include "HTML5AudioDevice.h"
|
2014-07-04 09:47:36 -04:00
|
|
|
#include "VorbisAudioInfo.h"
|
2014-03-14 14:13:41 -04:00
|
|
|
#include "AudioEffect.h"
|
|
|
|
|
#include "Engine.h"
|
|
|
|
|
|
2014-05-29 17:19:43 -04:00
|
|
|
DEFINE_LOG_CATEGORY(LogALAudio);
|
2014-03-14 14:13:41 -04:00
|
|
|
|
|
|
|
|
ALCdevice* FALAudioDevice::HardwareDevice = NULL;
|
|
|
|
|
ALCcontext* FALAudioDevice::SoundContext = NULL;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class FHTML5AudioDeviceModule : public IAudioDeviceModule
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
|
|
/** Creates a new instance of the audio device implemented by the module. */
|
2014-06-13 06:14:46 -04:00
|
|
|
virtual FAudioDevice* CreateAudioDevice() override
|
2014-03-14 14:13:41 -04:00
|
|
|
{
|
|
|
|
|
return new FALAudioDevice;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
IMPLEMENT_MODULE(FHTML5AudioDeviceModule, HTML5Audio );
|
|
|
|
|
/*------------------------------------------------------------------------------------
|
|
|
|
|
UALAudioDevice constructor and UObject interface.
|
|
|
|
|
------------------------------------------------------------------------------------*/
|
|
|
|
|
|
2014-05-29 17:19:43 -04:00
|
|
|
void FALAudioDevice::TeardownHardware( void )
|
2014-03-14 14:13:41 -04:00
|
|
|
{
|
|
|
|
|
// Flush stops all sources and deletes all buffers so sources can be safely deleted below.
|
|
|
|
|
Flush( NULL );
|
|
|
|
|
|
|
|
|
|
// Push any pending data to the hardware
|
|
|
|
|
if( &alcProcessContext )
|
|
|
|
|
{
|
|
|
|
|
alcProcessContext( SoundContext );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Destroy all sound sources
|
|
|
|
|
for( int i = 0; i < Sources.Num(); i++ )
|
|
|
|
|
{
|
|
|
|
|
delete Sources[ i ];
|
|
|
|
|
}
|
2014-05-29 17:19:43 -04:00
|
|
|
Sources.Empty();
|
|
|
|
|
FreeSources.Empty();
|
2014-03-14 14:13:41 -04:00
|
|
|
|
|
|
|
|
// Disable the context
|
|
|
|
|
if( &alcMakeContextCurrent )
|
|
|
|
|
{
|
|
|
|
|
alcMakeContextCurrent( NULL );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Destroy the context
|
|
|
|
|
if( &alcDestroyContext )
|
|
|
|
|
{
|
|
|
|
|
alcDestroyContext( SoundContext );
|
|
|
|
|
SoundContext = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Close the hardware device
|
|
|
|
|
if( &alcCloseDevice )
|
|
|
|
|
{
|
|
|
|
|
alcCloseDevice( HardwareDevice );
|
|
|
|
|
HardwareDevice = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*------------------------------------------------------------------------------------
|
|
|
|
|
UAudioDevice Interface.
|
|
|
|
|
------------------------------------------------------------------------------------*/
|
|
|
|
|
/**
|
|
|
|
|
* Initializes the audio device and creates sources.
|
|
|
|
|
*
|
|
|
|
|
* @warning:
|
|
|
|
|
*
|
|
|
|
|
* @return TRUE if initialization was successful, FALSE otherwise
|
|
|
|
|
*/
|
|
|
|
|
bool FALAudioDevice::InitializeHardware( void )
|
|
|
|
|
{
|
|
|
|
|
// Make sure no interface classes contain any garbage
|
|
|
|
|
Effects = NULL;
|
|
|
|
|
DLLHandle = NULL;
|
|
|
|
|
|
|
|
|
|
// Default to sensible channel count.
|
|
|
|
|
if( MaxChannels < 1 )
|
|
|
|
|
{
|
|
|
|
|
MaxChannels = 32;
|
|
|
|
|
}
|
|
|
|
|
// Load ogg and vorbis dlls if they haven't been loaded yet
|
|
|
|
|
//LoadVorbisLibraries();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Open device
|
|
|
|
|
HardwareDevice = alcOpenDevice( NULL );
|
|
|
|
|
if( !HardwareDevice )
|
|
|
|
|
{
|
2014-05-29 17:19:43 -04:00
|
|
|
UE_LOG(LogALAudio, Log, TEXT( "ALAudio: no OpenAL devices found." ) );
|
2014-03-14 14:13:41 -04:00
|
|
|
return false ;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Display the audio device that was actually opened
|
|
|
|
|
const ALCchar* OpenedDeviceName = alcGetString( HardwareDevice, ALC_DEVICE_SPECIFIER );
|
2014-05-29 17:19:43 -04:00
|
|
|
UE_LOG(LogALAudio, Log, TEXT("ALAudio device opened : %s"), StringCast<TCHAR>(static_cast<const ANSICHAR*>(OpenedDeviceName)).Get());
|
2014-03-14 14:13:41 -04:00
|
|
|
|
|
|
|
|
// Create a context
|
|
|
|
|
int Caps[] =
|
|
|
|
|
{
|
|
|
|
|
ALC_FREQUENCY, 44100,
|
|
|
|
|
ALC_STEREO_SOURCES, 4,
|
|
|
|
|
0, 0
|
|
|
|
|
};
|
|
|
|
|
#if PLATFORM_HTML5_WIN32
|
|
|
|
|
SoundContext = alcCreateContext( HardwareDevice, Caps );
|
|
|
|
|
#elif PLATFORM_HTML5
|
|
|
|
|
SoundContext = alcCreateContext( HardwareDevice, 0 );
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
if( !SoundContext )
|
|
|
|
|
{
|
|
|
|
|
return false ;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
alcMakeContextCurrent( SoundContext );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Make sure everything happened correctly
|
|
|
|
|
if( alError( TEXT( "Init" ) ) )
|
|
|
|
|
{
|
2014-05-29 17:19:43 -04:00
|
|
|
UE_LOG(LogALAudio, Warning, TEXT("ALAudio: alcMakeContextCurrent failed."));
|
2014-03-14 14:13:41 -04:00
|
|
|
return false ;
|
|
|
|
|
}
|
|
|
|
|
|
2014-05-29 17:19:43 -04:00
|
|
|
UE_LOG(LogALAudio, Log, TEXT("AL_VENDOR : %s"), StringCast<TCHAR>(static_cast<const ANSICHAR*>(alGetString(AL_VENDOR))).Get());
|
|
|
|
|
UE_LOG(LogALAudio, Log, TEXT("AL_RENDERER : %s"), StringCast<TCHAR>(static_cast<const ANSICHAR*>(alGetString(AL_RENDERER))).Get());
|
|
|
|
|
UE_LOG(LogALAudio, Log, TEXT("AL_VERSION : %s"), StringCast<TCHAR>(static_cast<const ANSICHAR*>(alGetString(AL_VERSION))).Get());
|
|
|
|
|
UE_LOG(LogALAudio, Log, TEXT("AL_EXTENSIONS : %s"), StringCast<TCHAR>(static_cast<const ANSICHAR*>(alGetString(AL_EXTENSIONS))).Get());
|
2014-03-14 14:13:41 -04:00
|
|
|
|
|
|
|
|
// Get the enums for multichannel support
|
|
|
|
|
#if !PLATFORM_HTML5
|
|
|
|
|
Surround40Format = alGetEnumValue( "AL_FORMAT_QUAD16" );
|
|
|
|
|
Surround51Format = alGetEnumValue( "AL_FORMAT_51CHN16" );
|
|
|
|
|
Surround61Format = alGetEnumValue( "AL_FORMAT_61CHN16" );
|
|
|
|
|
Surround71Format = alGetEnumValue( "AL_FORMAT_71CHN16" );
|
|
|
|
|
#endif
|
|
|
|
|
// Initialize channels.
|
|
|
|
|
alError( TEXT( "Emptying error stack" ), 0 );
|
|
|
|
|
for( int i = 0; i < (( MaxChannels > MAX_AUDIOCHANNELS ) ? MAX_AUDIOCHANNELS : MaxChannels); i++ )
|
|
|
|
|
{
|
|
|
|
|
ALuint SourceId;
|
|
|
|
|
alGenSources( 1, &SourceId );
|
|
|
|
|
if( !alError( TEXT( "Init (creating sources)" ), 0 ) )
|
|
|
|
|
{
|
|
|
|
|
FALSoundSource* Source = new FALSoundSource( this );
|
|
|
|
|
Source->SourceId = SourceId;
|
|
|
|
|
Sources.Add( Source );
|
|
|
|
|
FreeSources.Add( Source );
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( Sources.Num() < 1 )
|
|
|
|
|
{
|
2014-05-29 17:19:43 -04:00
|
|
|
UE_LOG(LogALAudio, Warning, TEXT("ALAudio: couldn't allocate any sources"));
|
2014-03-14 14:13:41 -04:00
|
|
|
return false ;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Update MaxChannels in case we couldn't create enough sources.
|
|
|
|
|
MaxChannels = Sources.Num();
|
2014-05-29 17:19:43 -04:00
|
|
|
UE_LOG(LogALAudio, Verbose, TEXT("ALAudioDevice: Allocated %i sources"), MaxChannels);
|
2014-03-14 14:13:41 -04:00
|
|
|
|
|
|
|
|
// Use our own distance model.
|
|
|
|
|
alDistanceModel( AL_NONE );
|
|
|
|
|
|
|
|
|
|
// Set up a default (nop) effects manager
|
|
|
|
|
Effects = new FAudioEffectsManager( this );
|
|
|
|
|
|
|
|
|
|
return true ;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Update the audio device and calculates the cached inverse transform later
|
|
|
|
|
* on used for spatialization.
|
|
|
|
|
*
|
|
|
|
|
* @param Realtime whether we are paused or not
|
|
|
|
|
*/
|
|
|
|
|
void FALAudioDevice::Update( bool Realtime )
|
|
|
|
|
{
|
|
|
|
|
FVector ListenerLocation = Listeners[ 0 ].Transform.GetLocation();
|
|
|
|
|
FVector ListenerFront = Listeners[ 0 ].GetFront();
|
|
|
|
|
FVector ListenerUp = Listeners[ 0 ].GetUp();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Set Player position
|
|
|
|
|
FVector Location;
|
|
|
|
|
|
|
|
|
|
// See file header for coordinate system explanation.
|
|
|
|
|
Location.X = ListenerLocation.X;
|
|
|
|
|
Location.Y = ListenerLocation.Z; // Z/Y swapped on purpose, see file header
|
|
|
|
|
Location.Z = ListenerLocation.Y; // Z/Y swapped on purpose, see file header
|
|
|
|
|
Location *= AUDIO_DISTANCE_FACTOR;
|
|
|
|
|
|
|
|
|
|
// Set Player orientation.
|
|
|
|
|
FVector Orientation[2];
|
|
|
|
|
|
|
|
|
|
// See file header for coordinate system explanation.
|
|
|
|
|
Orientation[0].X = ListenerFront.X;
|
|
|
|
|
Orientation[0].Y = ListenerFront.Z; // Z/Y swapped on purpose, see file header
|
|
|
|
|
Orientation[0].Z = ListenerFront.Y; // Z/Y swapped on purpose, see file header
|
|
|
|
|
|
|
|
|
|
// See file header for coordinate system explanation.
|
|
|
|
|
Orientation[1].X = ListenerUp.X;
|
|
|
|
|
Orientation[1].Y = ListenerUp.Z; // Z/Y swapped on purpose, see file header
|
|
|
|
|
Orientation[1].Z = ListenerUp.Y; // Z/Y swapped on purpose, see file header
|
|
|
|
|
|
|
|
|
|
// Make the listener still and the sounds move relatively -- this allows
|
|
|
|
|
// us to scale the doppler effect on a per-sound basis.
|
|
|
|
|
FVector Velocity = FVector( 0.0f, 0.0f, 0.0f );
|
|
|
|
|
|
2014-10-01 14:45:04 -04:00
|
|
|
alListenerfv( AL_POSITION, ( ALfloat* )&Location );
|
|
|
|
|
alListenerfv( AL_ORIENTATION, ( ALfloat* )&Orientation[0] );
|
|
|
|
|
alListenerfv( AL_VELOCITY, ( ALfloat* )&Velocity );
|
2014-03-14 14:13:41 -04:00
|
|
|
|
|
|
|
|
alError( TEXT( "UALAudioDevice::Update" ) );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ALuint FALAudioDevice::GetInternalFormat( int NumChannels )
|
|
|
|
|
{
|
|
|
|
|
ALuint InternalFormat = 0;
|
|
|
|
|
|
|
|
|
|
switch( NumChannels )
|
|
|
|
|
{
|
|
|
|
|
case 0:
|
|
|
|
|
case 3:
|
|
|
|
|
case 5:
|
|
|
|
|
break;
|
|
|
|
|
case 1:
|
|
|
|
|
InternalFormat = AL_FORMAT_MONO16;
|
|
|
|
|
break;
|
|
|
|
|
case 2:
|
|
|
|
|
InternalFormat = AL_FORMAT_STEREO16;
|
|
|
|
|
break;
|
|
|
|
|
#if !PLATFORM_HTML5
|
|
|
|
|
case 4:
|
|
|
|
|
InternalFormat = Surround40Format;
|
|
|
|
|
break;
|
|
|
|
|
case 6:
|
|
|
|
|
InternalFormat = Surround51Format;
|
|
|
|
|
break;
|
|
|
|
|
case 7:
|
|
|
|
|
InternalFormat = Surround61Format;
|
|
|
|
|
break;
|
|
|
|
|
case 8:
|
|
|
|
|
InternalFormat = Surround71Format;
|
|
|
|
|
break;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return( InternalFormat );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*------------------------------------------------------------------------------------
|
|
|
|
|
OpenAL utility functions
|
|
|
|
|
------------------------------------------------------------------------------------*/
|
|
|
|
|
//
|
|
|
|
|
// FindExt
|
|
|
|
|
//
|
|
|
|
|
bool FALAudioDevice::FindExt( const TCHAR* Name )
|
|
|
|
|
{
|
2014-05-29 17:19:43 -04:00
|
|
|
return alIsExtensionPresent(TCHAR_TO_ANSI(Name)) || alcIsExtensionPresent(HardwareDevice, TCHAR_TO_ANSI(Name));
|
2014-03-14 14:13:41 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// FindProc
|
|
|
|
|
//
|
|
|
|
|
void FALAudioDevice::FindProc( void*& ProcAddress, char* Name, char* SupportName, bool& Supports, bool AllowExt )
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// FindProcs
|
|
|
|
|
//
|
|
|
|
|
void FALAudioDevice::FindProcs( bool AllowExt )
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// alError
|
|
|
|
|
//
|
|
|
|
|
bool FALAudioDevice::alError( const TCHAR* Text, bool Log )
|
|
|
|
|
{
|
|
|
|
|
ALenum Error = alGetError();
|
|
|
|
|
if( Error != AL_NO_ERROR )
|
|
|
|
|
{
|
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
if( Log )
|
|
|
|
|
{
|
|
|
|
|
switch ( Error )
|
|
|
|
|
{
|
|
|
|
|
case AL_INVALID_NAME:
|
2014-05-29 17:19:43 -04:00
|
|
|
UE_LOG(LogALAudio, Warning, TEXT("ALAudio: AL_INVALID_NAME in %s"), Text);
|
2014-03-14 14:13:41 -04:00
|
|
|
break;
|
|
|
|
|
case AL_INVALID_VALUE:
|
2014-05-29 17:19:43 -04:00
|
|
|
UE_LOG(LogALAudio, Warning, TEXT("ALAudio: AL_INVALID_VALUE in %s"), Text);
|
2014-03-14 14:13:41 -04:00
|
|
|
break;
|
|
|
|
|
case AL_OUT_OF_MEMORY:
|
2014-05-29 17:19:43 -04:00
|
|
|
UE_LOG(LogALAudio, Warning, TEXT("ALAudio: AL_OUT_OF_MEMORY in %s"), Text);
|
2014-03-14 14:13:41 -04:00
|
|
|
break;
|
|
|
|
|
case AL_INVALID_ENUM:
|
2014-05-29 17:19:43 -04:00
|
|
|
UE_LOG(LogALAudio, Warning, TEXT("ALAudio: AL_INVALID_ENUM in %s"), Text);
|
2014-03-14 14:13:41 -04:00
|
|
|
break;
|
|
|
|
|
case AL_INVALID_OPERATION:
|
2014-05-29 17:19:43 -04:00
|
|
|
UE_LOG(LogALAudio, Warning, TEXT("ALAudio: AL_INVALID_OPERATION in %s"), Text);
|
2014-03-14 14:13:41 -04:00
|
|
|
break;
|
|
|
|
|
default:
|
2014-05-29 17:19:43 -04:00
|
|
|
UE_LOG(LogALAudio, Warning, TEXT("ALAudio: Unknown Error NUM in %s"), Text);
|
2014-03-14 14:13:41 -04:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
while( ( Error = alGetError() ) != AL_NO_ERROR );
|
|
|
|
|
|
|
|
|
|
return true ;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false ;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FSoundSource* FALAudioDevice::CreateSoundSource()
|
|
|
|
|
{
|
|
|
|
|
return new FALSoundSource(this);
|
|
|
|
|
}
|
2014-05-29 17:19:43 -04:00
|
|
|
|
|
|
|
|
bool FALAudioDevice::HasCompressedAudioInfoClass(USoundWave* SoundWave)
|
|
|
|
|
{
|
|
|
|
|
#if WITH_OGGVORBIS
|
|
|
|
|
return true;
|
|
|
|
|
#else
|
|
|
|
|
return false;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class ICompressedAudioInfo* FALAudioDevice::CreateCompressedAudioInfo(USoundWave* SoundWave)
|
|
|
|
|
{
|
|
|
|
|
#if WITH_OGGVORBIS
|
|
|
|
|
return new FVorbisAudioInfo();
|
|
|
|
|
#else
|
|
|
|
|
return NULL;
|
|
|
|
|
#endif
|
|
|
|
|
}
|