Files
UnrealEngineUWP/Engine/Source/Runtime/HTML5/HTML5Audio/Private/HTML5AudioSource.cpp
Matthew Griffin e529148280 Added config setting for audio volume when app is unfocused
Removed GVolumeMultiplier and added an equivalent to FApp to contain the code to load the unfocused volume multiplier from config.

UE-4449 - Allow users to choose whether audio can be heard when focus is lost

#codereview Robert.Manuszewski

[CL 2341022 by Matthew Griffin in Main branch]
2014-10-27 08:15: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 *= FApp::GetVolumeMultiplier();
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 );
}