Files
UnrealEngineUWP/Engine/Source/Runtime/ALAudio/Private/ALAudioSource.cpp
maxwell hayes 7484af145c Removing stereo bleed members. Not used in the new audio engine.
#rb Aaron.McLeran
#jira UE-87035


#ROBOMERGE-SOURCE: CL 11097936 via CL 11097944 via CL 11097952
#ROBOMERGE-BOT: (v640-11091645)

[CL 11097958 by maxwell hayes in Main branch]
2020-01-23 15:01:48 -05:00

292 lines
7.4 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
/*------------------------------------------------------------------------------------
FALSoundSource.
------------------------------------------------------------------------------------*/
#include "CoreMinimal.h"
#include "Stats/Stats.h"
#include "Misc/App.h"
#include "Audio.h"
#include "Sound/SoundWave.h"
#include "ALAudioDevice.h"
#include "ContentStreaming.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 )
{
FSoundSource::InitCommon();
check(InWaveInstance);
GetALDevice()->MakeCurrent(TEXT("FALSoundSource::Init()"));
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->GetUseSpatialization() ? 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->BufferId);
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->BufferId);
}
Update();
// Initialization was successful.
return true;
}
}
// Failed to initialize source.
UE_LOG(LogALAudio, Warning, TEXT("Failed to initialize sound source with WaveInstance '%s'."), *InWaveInstance->WaveData->GetName());
UE_LOG(LogALAudio, Warning, TEXT(" SampleRate %d"), InWaveInstance->WaveData->GetSampleRateForCurrentPlatform());
UE_LOG(LogALAudio, Warning, TEXT(" Channels %d"), InWaveInstance->WaveData->NumChannels);
return false;
}
/**
* Clean up any hardware referenced by the sound source
*/
FALSoundSource::~FALSoundSource( void )
{
GetALDevice()->MakeCurrent(TEXT("FALSoundSource::~FALSoundSource()"));
// @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;
}
FSoundSource::UpdateCommon();
GetALDevice()->MakeCurrent(TEXT("FALSoundSource::Update()"));
float Volume = 1.0f;
if (AudioDevice->IsAudioDeviceMuted())
{
Volume = 0.0f;
}
else
{
Volume = WaveInstance->GetActualVolume();
Volume *= AudioDevice->GetPlatformAudioHeadroom();
Volume = FMath::Clamp(Volume, 0.0f, MAX_VOLUME);
}
Volume = FSoundSource::GetDebugVolume(Volume);
// Set whether to apply reverb
SetReverbApplied( true );
SetFilterFrequency();
FVector Location;
// 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
// Convert to meters.
Location *= AUDIO_DISTANCE_FACTOR;
// We're using a relative coordinate system for un- spatialized sounds.
FVector RelativeDirection = FVector::ZeroVector;
if (WaveInstance->GetUseSpatialization())
{
FVector UnnormalizedDirection = ((FALAudioDevice*)AudioDevice)->InverseTransform.TransformPosition(WaveInstance->Location);
RelativeDirection = UnnormalizedDirection.GetSafeNormal();
}
else
{
Location = FVector(0.f, 0.f, 0.f);
}
FVector EmitterPosition;
EmitterPosition.X = RelativeDirection.Y;
EmitterPosition.Y = RelativeDirection.X;
EmitterPosition.Z = -RelativeDirection.Z;
alSourcef(SourceId, AL_GAIN, Volume);
alSourcef(SourceId, AL_PITCH, Pitch);
alSourcefv(SourceId, AL_POSITION, (ALfloat*)&EmitterPosition);
}
/**
* Plays the current wave instance.
*/
void FALSoundSource::Play( void )
{
if( WaveInstance )
{
GetALDevice()->MakeCurrent(TEXT("FALSoundSource::Play()"));
alSourcePlay( SourceId );
Paused = false;
Playing = true;
}
}
/**
* Stops the current wave instance and detaches it from the source.
*/
void FALSoundSource::Stop( void )
{
IStreamingManager::Get().GetAudioStreamingManager().RemoveStreamingSoundSource(this);
if( WaveInstance )
{
GetALDevice()->MakeCurrent(TEXT("FALSoundSource::Stop()"));
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 )
{
GetALDevice()->MakeCurrent(TEXT("FALSoundSource::Pause()"));
alSourcePause( SourceId );
Paused = true;
}
}
/**
* Returns TRUE if an OpenAL source has finished playing
*/
bool FALSoundSource::IsSourceFinished( void )
{
ALint State = AL_STOPPED;
GetALDevice()->MakeCurrent(TEXT("FALSoundSource::IsSourceFinished()"));
// 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 )
{
GetALDevice()->MakeCurrent(TEXT("FALSoundSource::HandleQueuedBuffer()"));
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->BufferId);
}
/**
* 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 )
{
GetALDevice()->MakeCurrent(TEXT("FALSoundSource::IsFinished()"));
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;
}