Files
UnrealEngineUWP/Engine/Source/Runtime/HTML5/HTML5Audio/Private/HTML5AudioSource.cpp
2014-03-14 14:13:41 -04:00

256 lines
6.4 KiB
C++

// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
/*------------------------------------------------------------------------------------
FALSoundSource.
------------------------------------------------------------------------------------*/
#include "HTML5AudioDevice.h"
#include "Engine.h"
/**
* Initializes a source with a given wave instance and prepares it for playback.
*
* @param WaveInstance wave instace being primed for playback
* @return TRUE if initialization was successful, FALSE otherwise
*/
bool FALSoundSource::Init( FWaveInstance* InWaveInstance )
{
if (InWaveInstance->OutputTarget != EAudioOutputTarget::Controller)
{
// Find matching buffer.
Buffer = FALSoundBuffer::Init( (FALAudioDevice*)AudioDevice, InWaveInstance->WaveData );
if( Buffer )
{
SCOPE_CYCLE_COUNTER( STAT_AudioSourceInitTime );
WaveInstance = InWaveInstance;
// Enable/disable spatialization of sounds
alSourcei( SourceId, AL_SOURCE_RELATIVE, WaveInstance->bUseSpatialization ? AL_FALSE : AL_TRUE );
// Setting looping on a real time decompressed source suppresses the buffers processed message
alSourcei( SourceId, AL_LOOPING, ( WaveInstance->LoopingMode == LOOP_Forever ) ? AL_TRUE : AL_FALSE );
// Always queue up the first buffer
alSourceQueueBuffers( SourceId, 1, Buffer->BufferIds );
if( WaveInstance->LoopingMode == LOOP_WithNotification )
{
// We queue the sound twice for wave instances that use seamless looping so we can have smooth
// loop transitions. The downside is that we might play at most one frame worth of audio from the
// beginning of the wave after the wave stops looping.
alSourceQueueBuffers( SourceId, 1, Buffer->BufferIds );
}
Update();
// Initialization was successful.
return true ;
}
}
// Failed to initialize source.
return false ;
}
/**
* Clean up any hardware referenced by the sound source
*/
FALSoundSource::~FALSoundSource( void )
{
// @todo openal: What do we do here
/// AudioDevice->DestroyEffect( this );
alDeleteSources( 1, &SourceId );
}
/**
* Updates the source specific parameter like e.g. volume and pitch based on the associated
* wave instance.
*/
void FALSoundSource::Update( void )
{
SCOPE_CYCLE_COUNTER( STAT_AudioUpdateSources );
if( !WaveInstance || Paused )
{
return;
}
float Volume = WaveInstance->Volume * WaveInstance->VolumeMultiplier;
if( SetStereoBleed() )
{
// Emulate the bleed to rear speakers followed by stereo fold down
Volume *= 1.25f;
}
Volume *= GVolumeMultiplier;
Volume = FMath::Clamp(Volume, 0.0f, MAX_VOLUME );
float Pitch = FMath::Clamp(WaveInstance->Pitch, MIN_PITCH, MAX_PITCH );
// Set whether to apply reverb
SetReverbApplied( true );
// Set the HighFrequencyGain value
SetHighFrequencyGain();
FVector Location;
FVector Velocity;
// See file header for coordinate system explanation.
Location.X = WaveInstance->Location.X;
Location.Y = WaveInstance->Location.Z; // Z/Y swapped on purpose, see file header
Location.Z = WaveInstance->Location.Y; // Z/Y swapped on purpose, see file header
Velocity.X = WaveInstance->Velocity.X;
Velocity.Y = WaveInstance->Velocity.Z; // Z/Y swapped on purpose, see file header
Velocity.Z = WaveInstance->Velocity.Y; // Z/Y swapped on purpose, see file header
// Convert to meters.
Location *= AUDIO_DISTANCE_FACTOR;
Velocity *= AUDIO_DISTANCE_FACTOR;
// We're using a relative coordinate system for un- spatialized sounds.
if( !WaveInstance->bUseSpatialization )
{
Location = FVector( 0.f, 0.f, 0.f );
}
alSourcef( SourceId, AL_GAIN, Volume );
alSourcef( SourceId, AL_PITCH, Pitch );
alSourcefv( SourceId, AL_POSITION, ( ALfloat * )&Location );
alSourcefv( SourceId, AL_VELOCITY, ( ALfloat * )&Velocity );
// Platform dependent call to update the sound output with new parameters
// @todo openal: Is this no longer needed?
/// AudioDevice->UpdateEffect( this );
}
/**
* Plays the current wave instance.
*/
void FALSoundSource::Play( void )
{
if( WaveInstance )
{
alSourcePlay( SourceId );
Paused = false;
Playing = true;
}
}
/**
* Stops the current wave instance and detaches it from the source.
*/
void FALSoundSource::Stop( void )
{
if( WaveInstance )
{
alSourceStop( SourceId );
// This clears out any pending buffers that may or may not be queued or played
alSourcei( SourceId, AL_BUFFER, 0 );
Paused = false;
Playing = false;
Buffer = NULL;
}
FSoundSource::Stop();
}
/**
* Pauses playback of current wave instance.
*/
void FALSoundSource::Pause( void )
{
if( WaveInstance )
{
alSourcePause( SourceId );
Paused = true;
}
}
/**
* Returns TRUE if an OpenAL source has finished playing
*/
bool FALSoundSource::IsSourceFinished( void )
{
ALint State = AL_STOPPED;
// Check the source for data to continue playing
alGetSourcei( SourceId, AL_SOURCE_STATE, &State );
if( State == AL_PLAYING || State == AL_PAUSED )
{
return( false );
}
return( true );
}
/**
* Handle dequeuing and requeuing of a single buffer
*/
void FALSoundSource::HandleQueuedBuffer( void )
{
ALuint DequeuedBuffer;
// Unqueue the processed buffers
alSourceUnqueueBuffers( SourceId, 1, &DequeuedBuffer );
// Notify the wave instance that the current (native) buffer has finished playing.
WaveInstance->NotifyFinished();
// Queue the same packet again for looping
alSourceQueueBuffers( SourceId, 1, Buffer->BufferIds );
}
/**
* Queries the status of the currently associated wave instance.
*
* @return TRUE if the wave instance/ source has finished playback and FALSE if it is
* currently playing or paused.
*/
bool FALSoundSource::IsFinished( void )
{
if( WaveInstance )
{
// Check for a non starved, stopped source
if( IsSourceFinished() )
{
// Notify the wave instance that it has finished playing.
WaveInstance->NotifyFinished();
return( true );
}
else
{
// Check to see if any complete buffers have been processed
ALint BuffersProcessed;
alGetSourcei( SourceId, AL_BUFFERS_PROCESSED, &BuffersProcessed );
switch( BuffersProcessed )
{
case 0:
// No buffers need updating
break;
case 1:
// Standard case of 1 buffer expired which needs repopulating
HandleQueuedBuffer();
break;
case 2:
// Starvation case when the source has stopped
HandleQueuedBuffer();
HandleQueuedBuffer();
// Restart the source
alSourcePlay( SourceId );
break;
}
}
return( false );
}
return( true );
}