2016-01-07 08:17:16 -05:00
|
|
|
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
|
2014-03-14 14:13:41 -04:00
|
|
|
|
|
|
|
|
/*------------------------------------------------------------------------------------
|
|
|
|
|
FSLESSoundSource.
|
|
|
|
|
------------------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
#include "AndroidAudioDevice.h"
|
|
|
|
|
#include "AudioDecompress.h"
|
|
|
|
|
#include "Engine.h"
|
|
|
|
|
|
|
|
|
|
// Callback that is registered if the source needs to loop
|
|
|
|
|
void OpenSLBufferQueueCallback( SLAndroidSimpleBufferQueueItf InQueueInterface, void* pContext )
|
|
|
|
|
{
|
|
|
|
|
FSLESSoundSource* SoundSource = (FSLESSoundSource*)pContext;
|
|
|
|
|
if( SoundSource )
|
|
|
|
|
{
|
|
|
|
|
SoundSource->OnRequeueBufferCallback( InQueueInterface );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Requeues buffer to loop Sound Source
|
|
|
|
|
void FSLESSoundSource::OnRequeueBufferCallback( SLAndroidSimpleBufferQueueItf InQueueInterface )
|
|
|
|
|
{
|
|
|
|
|
if (!bStreamedSound)
|
|
|
|
|
{
|
|
|
|
|
SLresult result = (*SL_PlayerBufferQueue)->Enqueue(SL_PlayerBufferQueue, Buffer->AudioData, Buffer->GetSize() );
|
|
|
|
|
if(result != SL_RESULT_SUCCESS)
|
|
|
|
|
{
|
|
|
|
|
UE_LOG( LogAndroidAudio, Warning, TEXT("FAILED OPENSL BUFFER Enqueue SL_PlayerBufferQueue (Requeing)"));
|
|
|
|
|
}
|
|
|
|
|
bHasLooped = true;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// Sound decoding is complete, just waiting to finish playing
|
2014-10-24 10:55:50 -04:00
|
|
|
if (bBuffersToFlush)
|
|
|
|
|
{
|
|
|
|
|
// set the player's state to stopped
|
|
|
|
|
SLresult result = (*SL_PlayerPlayInterface)->SetPlayState(SL_PlayerPlayInterface, SL_PLAYSTATE_STOPPED);
|
|
|
|
|
check(SL_RESULT_SUCCESS == result);
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
2014-03-14 14:13:41 -04:00
|
|
|
|
|
|
|
|
// Enqueue the previously decoded buffer
|
2015-06-05 16:09:35 -04:00
|
|
|
if (RealtimeAsyncTask)
|
|
|
|
|
{
|
|
|
|
|
RealtimeAsyncTask->EnsureCompletion();
|
|
|
|
|
switch(RealtimeAsyncTask->GetTask().GetTaskType())
|
|
|
|
|
{
|
|
|
|
|
case ERealtimeAudioTaskType::Decompress:
|
|
|
|
|
bHasLooped = RealtimeAsyncTask->GetTask().GetBufferLooped();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case ERealtimeAudioTaskType::Procedural:
|
|
|
|
|
AudioBuffers[BufferInUse].AudioDataSize = RealtimeAsyncTask->GetTask().GetBytesWritten();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
delete RealtimeAsyncTask;
|
|
|
|
|
RealtimeAsyncTask = nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-14 14:13:41 -04:00
|
|
|
SLresult result = (*SL_PlayerBufferQueue)->Enqueue(SL_PlayerBufferQueue, AudioBuffers[BufferInUse].AudioData, AudioBuffers[BufferInUse].AudioDataSize );
|
|
|
|
|
if(result != SL_RESULT_SUCCESS)
|
|
|
|
|
{
|
|
|
|
|
UE_LOG( LogAndroidAudio, Warning, TEXT("FAILED OPENSL BUFFER Enqueue SL_PlayerBufferQueue (Requeing)"));
|
|
|
|
|
}
|
|
|
|
|
|
2015-06-05 16:09:35 -04:00
|
|
|
// Switch to the next buffer and decode for the next time the callback fires if we didn't just get the last buffer
|
2014-03-14 14:13:41 -04:00
|
|
|
BufferInUse = !BufferInUse;
|
2015-06-05 16:09:35 -04:00
|
|
|
if (bHasLooped == false || WaveInstance->LoopingMode != LOOP_Never)
|
|
|
|
|
{
|
2015-12-10 16:56:55 -05:00
|
|
|
if (ReadMorePCMData(BufferInUse, (bSkipFirstBuffer ? EDataReadMode::AsynchronousSkipFirstFrame : EDataReadMode::Asynchronous)))
|
2015-06-05 16:09:35 -04:00
|
|
|
{
|
|
|
|
|
// If this is a synchronous source we may get notified immediately that we have looped
|
|
|
|
|
bHasLooped = true;
|
|
|
|
|
}
|
2015-12-10 16:56:55 -05:00
|
|
|
bSkipFirstBuffer = false;
|
2015-06-05 16:09:35 -04:00
|
|
|
}
|
2014-03-14 14:13:41 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool FSLESSoundSource::CreatePlayer()
|
|
|
|
|
{
|
|
|
|
|
// data info
|
|
|
|
|
SLDataLocator_AndroidSimpleBufferQueue LocationBuffer = { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 1 };
|
|
|
|
|
|
|
|
|
|
// PCM Info
|
|
|
|
|
SLDataFormat_PCM PCM_Format = { SL_DATAFORMAT_PCM, SLuint32(Buffer->NumChannels), SLuint32( Buffer->SampleRate * 1000 ),
|
|
|
|
|
SL_PCMSAMPLEFORMAT_FIXED_16, SL_PCMSAMPLEFORMAT_FIXED_16,
|
|
|
|
|
Buffer->NumChannels == 2 ? ( SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT ) : SL_SPEAKER_FRONT_CENTER,
|
|
|
|
|
SL_BYTEORDER_LITTLEENDIAN };
|
|
|
|
|
|
|
|
|
|
SLDataSource SoundDataSource = { &LocationBuffer, &PCM_Format};
|
|
|
|
|
|
|
|
|
|
// configure audio sink
|
|
|
|
|
SLDataLocator_OutputMix Output_Mix = { SL_DATALOCATOR_OUTPUTMIX, ((FSLESAudioDevice *)AudioDevice)->SL_OutputMixObject};
|
|
|
|
|
SLDataSink AudioSink = { &Output_Mix, NULL};
|
|
|
|
|
|
|
|
|
|
// create audio player
|
|
|
|
|
const SLInterfaceID ids[] = {SL_IID_BUFFERQUEUE, SL_IID_VOLUME};
|
|
|
|
|
const SLboolean req[] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
|
|
|
|
|
SLresult result = (*Device->SL_EngineEngine)->CreateAudioPlayer( Device->SL_EngineEngine, &SL_PlayerObject,
|
|
|
|
|
&SoundDataSource, &AudioSink, sizeof(ids) / sizeof(SLInterfaceID), ids, req );
|
|
|
|
|
if(result != SL_RESULT_SUCCESS)
|
|
|
|
|
{
|
2015-01-20 10:09:11 -05:00
|
|
|
UE_LOG(LogAndroidAudio, Warning, TEXT("FAILED OPENSL BUFFER CreateAudioPlayer 0x%x"), result);
|
2014-03-14 14:13:41 -04:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool bFailedSetup = false;
|
|
|
|
|
|
|
|
|
|
// realize the player
|
|
|
|
|
result = (*SL_PlayerObject)->Realize(SL_PlayerObject, SL_BOOLEAN_FALSE);
|
2015-01-20 10:09:11 -05:00
|
|
|
if (result != SL_RESULT_SUCCESS) { UE_LOG(LogAndroidAudio, Warning, TEXT("FAILED OPENSL BUFFER Realize 0x%x"), result); return false; }
|
2014-03-14 14:13:41 -04:00
|
|
|
|
|
|
|
|
// get the play interface
|
|
|
|
|
result = (*SL_PlayerObject)->GetInterface(SL_PlayerObject, SL_IID_PLAY, &SL_PlayerPlayInterface);
|
2015-01-20 10:09:11 -05:00
|
|
|
if (result != SL_RESULT_SUCCESS) { UE_LOG(LogAndroidAudio, Warning, TEXT("FAILED OPENSL BUFFER GetInterface SL_IID_PLAY 0x%x"), result); bFailedSetup |= true; }
|
2014-03-14 14:13:41 -04:00
|
|
|
// volume
|
|
|
|
|
result = (*SL_PlayerObject)->GetInterface(SL_PlayerObject, SL_IID_VOLUME, &SL_VolumeInterface);
|
2015-01-20 10:09:11 -05:00
|
|
|
if (result != SL_RESULT_SUCCESS) { UE_LOG(LogAndroidAudio, Warning, TEXT("FAILED OPENSL BUFFER GetInterface SL_IID_VOLUME 0x%x"), result); bFailedSetup |= true; }
|
2014-03-14 14:13:41 -04:00
|
|
|
// buffer system
|
|
|
|
|
result = (*SL_PlayerObject)->GetInterface(SL_PlayerObject, SL_IID_BUFFERQUEUE, &SL_PlayerBufferQueue);
|
2015-01-20 10:09:11 -05:00
|
|
|
if (result != SL_RESULT_SUCCESS) { UE_LOG(LogAndroidAudio, Warning, TEXT("FAILED OPENSL BUFFER GetInterface SL_IID_BUFFERQUEUE 0x%x"), result); bFailedSetup |= true; }
|
|
|
|
|
|
2014-03-14 14:13:41 -04:00
|
|
|
return bFailedSetup == false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FSLESSoundSource::DestroyPlayer()
|
|
|
|
|
{
|
|
|
|
|
if( SL_PlayerObject )
|
|
|
|
|
{
|
|
|
|
|
// close it down...
|
|
|
|
|
(*SL_PlayerObject)->Destroy(SL_PlayerObject);
|
|
|
|
|
SL_PlayerObject = NULL;
|
|
|
|
|
SL_PlayerPlayInterface = NULL;
|
|
|
|
|
SL_PlayerBufferQueue = NULL;
|
|
|
|
|
SL_VolumeInterface = NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool FSLESSoundSource::EnqueuePCMBuffer( bool bLoop)
|
|
|
|
|
{
|
|
|
|
|
SLresult result;
|
|
|
|
|
// If looping, register a callback to requeue the buffer
|
|
|
|
|
if( bLoop )
|
|
|
|
|
{
|
|
|
|
|
result = (*SL_PlayerBufferQueue)->RegisterCallback(SL_PlayerBufferQueue, OpenSLBufferQueueCallback, (void*)this);
|
2015-01-20 10:09:11 -05:00
|
|
|
if (result != SL_RESULT_SUCCESS) { UE_LOG(LogAndroidAudio, Warning, TEXT("FAILED OPENSL BUFFER QUEUE RegisterCallback 0x%x "), result); return false; }
|
2014-03-14 14:13:41 -04:00
|
|
|
}
|
2014-08-18 09:13:38 -04:00
|
|
|
|
2014-03-14 14:13:41 -04:00
|
|
|
result = (*SL_PlayerBufferQueue)->Enqueue(SL_PlayerBufferQueue, Buffer->AudioData, Buffer->GetSize() );
|
2015-01-20 10:09:11 -05:00
|
|
|
if (result != SL_RESULT_SUCCESS) { UE_LOG(LogAndroidAudio, Warning, TEXT("FAILED OPENSL BUFFER Enqueue SL_PlayerBufferQueue 0x%x params( %p, %d)"), result, Buffer->AudioData, int32(Buffer->GetSize())); return false; }
|
2014-03-14 14:13:41 -04:00
|
|
|
|
|
|
|
|
bStreamedSound = false;
|
|
|
|
|
bHasLooped = false;
|
|
|
|
|
bBuffersToFlush = false;
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2015-06-05 16:09:35 -04:00
|
|
|
bool FSLESSoundSource::ReadMorePCMData(const int32 BufferIndex, EDataReadMode DataReadMode)
|
2014-08-28 11:18:51 -04:00
|
|
|
{
|
2015-06-05 16:09:35 -04:00
|
|
|
USoundWave* WaveData = WaveInstance->WaveData;
|
2014-08-28 11:18:51 -04:00
|
|
|
if (WaveData && WaveData->bProcedural)
|
|
|
|
|
{
|
2015-06-05 16:09:35 -04:00
|
|
|
const int32 MaxSamples = BufferSize / sizeof(int16);
|
|
|
|
|
if (DataReadMode == EDataReadMode::Synchronous || WaveData->bCanProcessAsync == false)
|
|
|
|
|
{
|
|
|
|
|
const int32 BytesWritten = WaveData->GeneratePCMData(AudioBuffers[BufferIndex].AudioData, MaxSamples);
|
|
|
|
|
AudioBuffers[BufferIndex].AudioDataSize = BytesWritten;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
RealtimeAsyncTask = new FAsyncRealtimeAudioTask(WaveData, AudioBuffers[BufferIndex].AudioData, MaxSamples);
|
|
|
|
|
RealtimeAsyncTask->StartBackgroundTask();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// we're never actually "looping" here.
|
|
|
|
|
return false;
|
2014-08-28 11:18:51 -04:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2015-06-05 16:09:35 -04:00
|
|
|
if (DataReadMode == EDataReadMode::Synchronous)
|
|
|
|
|
{
|
|
|
|
|
return Buffer->ReadCompressedData(AudioBuffers[BufferIndex].AudioData, WaveInstance->LoopingMode != LOOP_Never);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
RealtimeAsyncTask = new FAsyncRealtimeAudioTask(Buffer, AudioBuffers[BufferIndex].AudioData, WaveInstance->LoopingMode != LOOP_Never, DataReadMode == EDataReadMode::AsynchronousSkipFirstFrame);
|
|
|
|
|
RealtimeAsyncTask->StartBackgroundTask();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2014-08-28 11:18:51 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2014-03-14 14:13:41 -04:00
|
|
|
bool FSLESSoundSource::EnqueuePCMRTBuffer( bool bLoop )
|
|
|
|
|
{
|
|
|
|
|
if (AudioBuffers[0].AudioData || AudioBuffers[1].AudioData)
|
|
|
|
|
{
|
|
|
|
|
UE_LOG( LogAndroidAudio, Warning, TEXT("Enqueue PCMRT with buffers already allocated"));
|
|
|
|
|
}
|
|
|
|
|
FMemory::Memzero( AudioBuffers, sizeof( SLESAudioBuffer ) * 2 );
|
|
|
|
|
|
|
|
|
|
// Set up double buffer area to decompress to
|
2014-08-28 11:18:51 -04:00
|
|
|
BufferSize = Buffer->GetRTBufferSize() * Buffer->NumChannels;
|
2014-03-14 14:13:41 -04:00
|
|
|
|
2014-08-28 11:18:51 -04:00
|
|
|
AudioBuffers[0].AudioData = (uint8*)FMemory::Malloc(BufferSize);
|
|
|
|
|
AudioBuffers[0].AudioDataSize = BufferSize;
|
2014-08-18 09:13:38 -04:00
|
|
|
|
2014-08-28 11:18:51 -04:00
|
|
|
AudioBuffers[1].AudioData = (uint8*)FMemory::Malloc(BufferSize);
|
|
|
|
|
AudioBuffers[1].AudioDataSize = BufferSize;
|
2014-03-14 14:13:41 -04:00
|
|
|
|
2015-06-05 16:09:35 -04:00
|
|
|
// Only use the cached data if we're starting from the beginning, otherwise we'll have to take a synchronous hit
|
2015-12-10 16:56:55 -05:00
|
|
|
bSkipFirstBuffer = false;
|
2015-06-05 16:09:35 -04:00
|
|
|
if (WaveInstance->WaveData && WaveInstance->WaveData->CachedRealtimeFirstBuffer && WaveInstance->StartTime == 0.f)
|
|
|
|
|
{
|
|
|
|
|
FMemory::Memcpy((uint8*)AudioBuffers[0].AudioData, WaveInstance->WaveData->CachedRealtimeFirstBuffer, BufferSize);
|
2015-12-10 16:56:55 -05:00
|
|
|
FMemory::Memcpy((uint8*)AudioBuffers[1].AudioData, WaveInstance->WaveData->CachedRealtimeFirstBuffer + BufferSize, BufferSize);
|
|
|
|
|
bSkipFirstBuffer = true;
|
2015-06-05 16:09:35 -04:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
ReadMorePCMData(0, EDataReadMode::Synchronous);
|
2015-06-16 11:13:47 -04:00
|
|
|
ReadMorePCMData(1, EDataReadMode::Asynchronous);
|
2015-06-05 16:09:35 -04:00
|
|
|
}
|
|
|
|
|
|
2014-03-14 14:13:41 -04:00
|
|
|
SLresult result;
|
|
|
|
|
|
|
|
|
|
// callback is used to submit and decompress next buffer
|
|
|
|
|
result = (*SL_PlayerBufferQueue)->RegisterCallback(SL_PlayerBufferQueue, OpenSLBufferQueueCallback, (void*)this);
|
|
|
|
|
|
|
|
|
|
// queue one sound buffer, as that is all Android will accept
|
|
|
|
|
if(result == SL_RESULT_SUCCESS)
|
|
|
|
|
{
|
|
|
|
|
result = (*SL_PlayerBufferQueue)->Enqueue(SL_PlayerBufferQueue, AudioBuffers[0].AudioData, AudioBuffers[0].AudioDataSize );
|
2015-01-20 10:09:11 -05:00
|
|
|
if (result != SL_RESULT_SUCCESS) { UE_LOG(LogAndroidAudio, Warning, TEXT("FAILED OPENSL BUFFER Enqueue SL_PlayerBufferQueue 0x%x params( %p, %d)"), result, Buffer->AudioData, int32(Buffer->GetSize())); return false; }
|
2014-03-14 14:13:41 -04:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bStreamedSound = true;
|
|
|
|
|
bHasLooped = false;
|
|
|
|
|
bBuffersToFlush = false;
|
|
|
|
|
BufferInUse = 1;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Initializes a source with a given wave instance and prepares it for playback.
|
|
|
|
|
*
|
2015-01-20 10:09:11 -05:00
|
|
|
* @param WaveInstance wave instance being primed for playback
|
2014-03-14 14:13:41 -04:00
|
|
|
* @return TRUE if initialization was successful, FALSE otherwise
|
|
|
|
|
*/
|
|
|
|
|
bool FSLESSoundSource::Init( FWaveInstance* InWaveInstance )
|
|
|
|
|
{
|
|
|
|
|
// don't do anything if no volume! THIS APPEARS TO HAVE THE VOLUME IN TIME, CHECK HERE THOUGH IF ISSUES
|
|
|
|
|
if( InWaveInstance && ( InWaveInstance->Volume * InWaveInstance->VolumeMultiplier ) <= 0 )
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (Buffer && Buffer->ResourceID == 0)
|
|
|
|
|
{
|
|
|
|
|
UE_LOG( LogAndroidAudio, Warning, TEXT(" InitSoundSouce with Buffer already allocated"));
|
|
|
|
|
delete Buffer;
|
|
|
|
|
Buffer = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (SL_PlayerObject)
|
|
|
|
|
{
|
|
|
|
|
UE_LOG( LogAndroidAudio, Warning, TEXT(" InitSoundSouce with PlayerObject not NULL, possible leak"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Find matching buffer.
|
|
|
|
|
Buffer = FSLESSoundBuffer::Init( (FSLESAudioDevice *)AudioDevice, InWaveInstance->WaveData );
|
|
|
|
|
|
|
|
|
|
if( Buffer && InWaveInstance->WaveData->NumChannels <= 2 && InWaveInstance->WaveData->SampleRate <= 48000 )
|
|
|
|
|
{
|
|
|
|
|
SCOPE_CYCLE_COUNTER( STAT_AudioSourceInitTime );
|
|
|
|
|
|
|
|
|
|
bool bFailedSetup = false;
|
|
|
|
|
|
|
|
|
|
if (CreatePlayer())
|
|
|
|
|
{
|
2015-01-20 10:09:11 -05:00
|
|
|
WaveInstance = InWaveInstance;
|
|
|
|
|
|
2015-12-10 16:56:55 -05:00
|
|
|
if (WaveInstance->StartTime > 0.f)
|
|
|
|
|
{
|
|
|
|
|
Buffer->Seek(WaveInstance->StartTime);
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-14 14:13:41 -04:00
|
|
|
switch( Buffer->Format)
|
|
|
|
|
{
|
|
|
|
|
case SoundFormat_PCM:
|
|
|
|
|
bFailedSetup |= !EnqueuePCMBuffer( InWaveInstance->LoopingMode != LOOP_Never );
|
|
|
|
|
break;
|
|
|
|
|
case SoundFormat_PCMRT:
|
|
|
|
|
bFailedSetup |= !EnqueuePCMRTBuffer( InWaveInstance->LoopingMode != LOOP_Never );
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
bFailedSetup = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
bFailedSetup = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// clean up the madness if anything we need failed
|
|
|
|
|
if( bFailedSetup )
|
|
|
|
|
{
|
|
|
|
|
UE_LOG( LogAndroidAudio, Warning, TEXT(" Setup failed %s"), *InWaveInstance->WaveData->GetName());
|
|
|
|
|
DestroyPlayer();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Update();
|
|
|
|
|
|
|
|
|
|
// Initialization was successful.
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// Failed to initialize source.
|
|
|
|
|
// These occurences appear to potentially lead to leaks
|
|
|
|
|
UE_LOG( LogAndroidAudio, Warning, TEXT("Init SoundSource failed on %s"), *InWaveInstance->WaveData->GetName());
|
|
|
|
|
UE_LOG( LogAndroidAudio, Warning, TEXT(" SampleRate %d"), InWaveInstance->WaveData->SampleRate);
|
|
|
|
|
UE_LOG( LogAndroidAudio, Warning, TEXT(" Channels %d"), InWaveInstance->WaveData->NumChannels);
|
|
|
|
|
|
|
|
|
|
if (Buffer && Buffer->ResourceID == 0)
|
|
|
|
|
{
|
|
|
|
|
delete Buffer;
|
|
|
|
|
Buffer = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
FSLESSoundSource::FSLESSoundSource( class FAudioDevice* InAudioDevice )
|
|
|
|
|
: FSoundSource( InAudioDevice ),
|
|
|
|
|
Device((FSLESAudioDevice *)InAudioDevice),
|
|
|
|
|
Buffer( NULL ),
|
|
|
|
|
bStreamedSound(false),
|
|
|
|
|
bBuffersToFlush(false),
|
2014-08-28 11:18:51 -04:00
|
|
|
BufferSize(0),
|
2014-03-14 14:13:41 -04:00
|
|
|
BufferInUse(0),
|
2015-07-13 16:50:26 -04:00
|
|
|
VolumePreviousUpdate(-1.0f),
|
2014-03-14 14:13:41 -04:00
|
|
|
bHasLooped(false),
|
|
|
|
|
SL_PlayerObject(NULL),
|
|
|
|
|
SL_PlayerPlayInterface(NULL),
|
|
|
|
|
SL_PlayerBufferQueue(NULL),
|
2015-06-19 03:58:19 -04:00
|
|
|
SL_VolumeInterface(NULL),
|
|
|
|
|
RealtimeAsyncTask(NULL)
|
2014-03-14 14:13:41 -04:00
|
|
|
{
|
|
|
|
|
FMemory::Memzero( AudioBuffers, sizeof( AudioBuffers ) );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Clean up any hardware referenced by the sound source
|
|
|
|
|
*/
|
|
|
|
|
FSLESSoundSource::~FSLESSoundSource( void )
|
|
|
|
|
{
|
|
|
|
|
DestroyPlayer();
|
|
|
|
|
|
|
|
|
|
ReleaseResources();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FSLESSoundSource::ReleaseResources()
|
|
|
|
|
{
|
2015-06-05 16:09:35 -04:00
|
|
|
if (RealtimeAsyncTask)
|
|
|
|
|
{
|
|
|
|
|
RealtimeAsyncTask->EnsureCompletion();
|
|
|
|
|
delete RealtimeAsyncTask;
|
|
|
|
|
RealtimeAsyncTask = nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-14 14:13:41 -04:00
|
|
|
FMemory::Free( AudioBuffers[0].AudioData);
|
|
|
|
|
FMemory::Free( AudioBuffers[1].AudioData);
|
|
|
|
|
|
|
|
|
|
FMemory::Memzero( AudioBuffers, sizeof( AudioBuffers ) );
|
|
|
|
|
|
|
|
|
|
if (Buffer && Buffer->ResourceID == 0)
|
|
|
|
|
{
|
|
|
|
|
delete Buffer;
|
|
|
|
|
}
|
|
|
|
|
Buffer = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Updates the source specific parameter like e.g. volume and pitch based on the associated
|
|
|
|
|
* wave instance.
|
|
|
|
|
*/
|
|
|
|
|
void FSLESSoundSource::Update( void )
|
|
|
|
|
{
|
|
|
|
|
SCOPE_CYCLE_COUNTER( STAT_AudioUpdateSources );
|
|
|
|
|
|
|
|
|
|
if( !WaveInstance || Paused )
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
2014-05-29 17:45:17 -04:00
|
|
|
|
2015-05-30 15:05:31 -04:00
|
|
|
float Volume = WaveInstance->Volume * WaveInstance->VolumeMultiplier;
|
|
|
|
|
if (SetStereoBleed())
|
2014-03-14 14:13:41 -04:00
|
|
|
{
|
2015-05-30 15:05:31 -04:00
|
|
|
// Emulate the bleed to rear speakers followed by stereo fold down
|
|
|
|
|
Volume *= 1.25f;
|
2014-03-14 14:13:41 -04:00
|
|
|
}
|
2015-05-30 15:05:31 -04:00
|
|
|
Volume *= FApp::GetVolumeMultiplier();
|
2015-07-13 16:50:26 -04:00
|
|
|
Volume *= AudioDevice->PlatformAudioHeadroom;
|
2015-05-30 15:05:31 -04:00
|
|
|
Volume = FMath::Clamp(Volume, 0.0f, MAX_VOLUME);
|
2014-05-29 17:45:17 -04:00
|
|
|
|
2015-05-29 15:01:29 -04:00
|
|
|
const float Pitch = FMath::Clamp<float>(WaveInstance->Pitch, MIN_PITCH, MAX_PITCH);
|
2014-05-22 17:20:22 -04:00
|
|
|
|
|
|
|
|
// Set whether to apply reverb
|
|
|
|
|
SetReverbApplied(true);
|
|
|
|
|
|
Copying //UE4/Dev-Framework to //UE4/Main @ 2775446
#lockdown Nick.Penwarden
==========================
MAJOR FEATURES + CHANGES
==========================
Change 2720537 on 2015/10/07 by Aaron.McLeran
Audio Optimization: Implementing an audio update delta time so audio isn't updated with every engine tick
Change 2746582 on 2015/10/29 by Aaron.McLeran
Implementing single ray-cast sound occlusion
- Fixing/Implementing sound occlusion using one ray cast
- Update rate of the ray cast is conifgurable by the user
- User can enabled/disable occlusion and set various properties of the occlusion at the audio component level
- Occlusion ray-casts are made at a slower rate than the audio engine is updated
- The following properties are supported:
* LowPassFilterFrequency (the LPF cutoff frequency of the per-voice filter to apply if a sound is occluded)
* OcclusionVolumeAttenuation (the volume attenuation to apply to sounds that are occluded)
* OcclusionInterpolationTime (how fast a sound moves from occluded to unoccluded... allows user-defined interpolation times, avoids fast parameter setting for smoother transitions)
- Fixed how low-pass filter frequency is applied on platforms that support it.
* Changed the older HighFrequencyGain parameter to a more understandable and correct LowPassFilterFrequency parameter
* Updated the parameter for the various other features that use per-voice LPF (e.g. audio volumes, distance-based filtering)
* Added backward-compatibility code to take old HighFrequencyGain params to new LowPassFilterFrequency params.
* At the lowest level, there is only one filter which is applied to a sound, but the parameter that has the lowest cutoff frequency is the one used
* Fixed how the parameter is applied at the lower-level in XAudio2 and CoreAudio. The old parameter was incorrectly applied.
* XAudio2 documentation on the low-pass-fitler frequency cutoff param is confirmed incorrect through testing.
#rb zak.middleton
Change 2765174 on 2015/11/12 by Aaron.McLeran
UE-23091 More fixage for NaN audio log spam
Last checkin for this (CL 2760896) was fixing a legit issue but there was still an issue that I was rarely catching during the FN gate encounter in AITestbed after about 5 min of gate defense.
To track down, I added a lot more logging and asserts on NaN numbers in the audio code, enabled ENABLE_NAN_DIAGNOSTIC in UnrealMathUtility.h.
- Was able to trace another cause of the NaNs in audio to 2 uninitialized variables in SoundAttenuationSettings struct: OmniRadius and StereoSpread.
- Zak M suggested the change of the const ref position vector in ListenerPosition and the usage of GetSafeNormal() in Audio.cpp since the value returned by GetLocation() is a temporary and Normalize() is unsafe.
- I removed the flag for the XAudio2 call to compute doppler since we don't use that value and it could've been as source of more NaNs in X3DAudioCalculate
#codereview Zak.Middleton
#rb Zak.Middleton
Change 2765467 on 2015/11/13 by James.Golding
- Allow -ms option to work with 'stat dumphitches' for controlling min time logged
#rb gil.gribb
Change 2765746 on 2015/11/13 by Benn.Gallagher
Added buckets for update rate shift tags (for staggering anim updates)
#jira UE-23213
#rb Bruce.Nesbit
Change 2765747 on 2015/11/13 by Benn.Gallagher
Fixes for bone length calculation in anim dynamics chains.
#rb Bruce.Nesbit
Change 2765749 on 2015/11/13 by Benn.Gallagher
Removed allocations from local CS blends for skeletal controls.
#rb Bruce.Nesbit
Change 2765752 on 2015/11/13 by Benn.Gallagher
Lod mapping support for URO customization
#jira UE-23211
#rb Bruce.Nesbit
Change 2765965 on 2015/11/13 by Marc.Audy
Remove outdated code in LoadMap previously used for matching up downloaded packages from servers. These code paths no longer operate, but in the case where a PIE client was connecting to a non-PIE server (not really supported, but possible), it would cause the Editor Level package to be flagged as as PIE package and cause a crash on exit of PIE
#jira UE-21181
#rb Josh.Markiewicz
Change 2766071 on 2015/11/13 by Mieszko.Zielinski
Fixed dumb mistake in AEQSTestingPawn::PostLoad #UE4
the 'bTickDuringGame == bTickDuringGame' thing
#rb John.Abercrombie
Change 2766086 on 2015/11/13 by Mieszko.Zielinski
Exposing NavModifierComponent to ENGINE_API #UE4
#codereview Lukasz.Furman
Change 2766345 on 2015/11/13 by Mieszko.Zielinski
No longer compiling AISystem's creation out from client builds #UE4
Instead AISystem's instantiation on clients is configurable via UAISystemBase::bInstantiateAISystemOnClient config variable
Change 2766346 on 2015/11/13 by Mieszko.Zielinski
Improvements to EQS test scoring function preview #UE4
#rb Lukasz.Furman
Change 2766528 on 2015/11/13 by Stan.Melax
multiple materials/chunks on a single cloth sim mesh
This supposedly used to work in 4.7 but broke for 4.8
It looks like previous change 2493547 may have introduced the condition statement that makes multiple materials not include all of there verts.
Changing this to instead check for the bounds on the skinningmap instead of the NumRealSimVerts which doesn't look like it takes the offset into consideration.
jira:
#UE-23300
https://jira.ol.epicgames.net/browse/UE-23300
https://answers.unrealengine.com/questions/287833/apex-not-working-on-multiple-materials-since-48.html
Double tested code change with test asset from https://jira.ol.epicgames.net/browse/UE-10674
#rb Benn.Gallagher
Change 2766546 on 2015/11/13 by Marc.Audy
Throw warning for orphaned looping sounds caused by a looping sound node
Don't do somewhat expensive (due to weak pointer) orphaned sound checks in test or shipping
#rb Aaron.McLeran
Change 2766917 on 2015/11/14 by Jurre.deBaare
Fix UE4-23349 Simplygon doens't support sub 64 pixel textures
Change 2767742 on 2015/11/16 by Marc.Audy
Restructure SignificanceManager to store significance manager references in a FGCObject Module class instead of using a singleton.
Improve performance by eliminating StaticFindObjectFast calls in ::Get and force inlining Get calls
#rb Zak.Middleton, Rob.Manuszewski
Change 2767827 on 2015/11/16 by Zak.Middleton
#ue4 - Perf: avoid SmoothClientPosition() calls once the target mesh offset has been reached.
- New flag bNetworkSmoothingComplete indicates whether smoothing has reached the destination. This is set to false when a new network position is received.
- Also fixes trying to smooth rotation towards identity rotation before receiving network updates.
- Deprecated FNetworkPredictionData_Client_Character::CurrentSmoothTime in favor of saving last update time, to prevent needing to update time every tick (since we skip updates now).
#rb Dan.Youhon
Change 2768070 on 2015/11/16 by Marc.Audy
Change StaticDuplicateObject and DuplicateObject to take FName instead of char* (existing usages can automatically convert to FName so no backwards compatibility issues)
Fixup usages to avoid unnecessary string -> char* -> name conversions
Change AActor::CreateComponentFromTemplate to take an FName instead of FString, deprecated FString version
#rb Gil.Gribb
Change 2768121 on 2015/11/16 by Marc.Audy
Forceinline GetFName
Change 2768161 on 2015/11/16 by Aaron.McLeran
Checking occlusion back in
- Bug was due to improper interpolation of LPFFrequency values in audio volumes.
Change 2769428 on 2015/11/17 by Thomas.Sarkanen
Fixed backwards-compatibility issues with FExposedValueCopyRecord
Added PostSerialize function to patch up older data to the new format (copies properties->property FNames).
Made sure to zero FExposedValueCopyRecord in its constructor, prevents uninitialized variable issues when generating CRCs. This is necessary despite these members being UPROPERTYs because they are simply built on the stack then stuffed into the new CDO during compilation.
UE-23268 - EDITOR CRASH: Assertion failed: ((UObject*)ContainerPtr)->IsA((UClass*)GetOuter())
#rb James.Golding
#codereview Mike.Beach,Bob.Tellez
Change 2769488 on 2015/11/17 by Benn.Gallagher
DrawCanvas and debug string support for skeletal controls - accessed using "Skeletal Controls" option in Show->Display Info in Persona
#rb Martin.Wilson
Change 2769545 on 2015/11/17 by Benn.Gallagher
Added space conversion options to copy bone node.
#jira OR-9430
#rb Martin.Wilson
Change 2770228 on 2015/11/17 by Marc.Audy
Fix cause of assert firing when detaching if AttachTo(AttachParent) returns false
#rb Ori.Cohen
#jira UE-23366
Change 2770489 on 2015/11/17 by Marc.Audy
Make ::Serialize WITH_EDITORONLY_DATA since it did nothing otherwise
Change 2770761 on 2015/11/17 by Aaron.McLeran
Removing optimization disablement
- Forgot to remove these lines before checking in/testing.
Change 2771375 on 2015/11/18 by Thomas.Sarkanen
Added initialzation of internal state machine desc ptrs to (finally) remove random crashes when using sub-state machines
#rb Benn.Gallagher
Change 2771697 on 2015/11/18 by Jeff.Farris
Sensible defaults for APlayerController::ClientPlayCameraAnim and ClientPlayCameraShake
#rb marc.audy
Change 2771755 on 2015/11/18 by Marc.Audy
Put back in the recursion block for destroy actor
#rb James.Golding
Change 2772217 on 2015/11/18 by Marc.Audy
Update axis inversion to work the same way as sensitivity instead of old problematic way
Also change to use Reset instead of Empty(Array.Num())
#rb Jeff.Farris
Change 2772686 on 2015/11/18 by Aaron.McLeran
Fixing HRTF spatialization coordinate bug.
Don't need to convert to xaudio2 coordinates for HRTF spatialization
#rb chad.taylor
Change 2772766 on 2015/11/18 by Aaron.McLeran
Fixing HRTF spatialization with xaudio2 voice pools
- Issue was caused by 2 issues:
- effect chains weren't properly clearing their state between uses (SetEffectChain(nullptr) releases the effect for use with other voices)
- Not re-setting MaxEffectChainChannels = 0 in CreateSource() was causing re-used SoundSources to release their XAudio2 voices to the wrong pool.
#rb chad.taylor
Change 2773027 on 2015/11/19 by Thomas.Sarkanen
PR #1765: Git Plugin can find the git.exe binary installed on the Local AppData user directory (Contributed by SRombauts)
#rb Thomas.Sarkanen
Change 2773142 on 2015/11/19 by Benn.Gallagher
Optimized SafeSetCSBoneTransforms, no longer doing 2 iterations over the whole pose.
- Only iterating beyond the index of the first bone transform, cutting off all bones before
- No longer using a bone mask the size of the skeleton, just an array of interesting nodes
- No longer attempting to convert a transform if it isn't necessary (which was happening a lot)
#rb Laurent.Delayen
Change 2773212 on 2015/11/19 by Richard.Hinckley
#jira UEDOC-2685
Removing ugly comment. Not going to remove the node itself for backward compatibility.
Change 2773351 on 2015/11/19 by Zak.Middleton
#orion - Make sure we don't skip a final visual update when character mesh interpolation tries to stop smoothing once it reaches its destination. We keep setting bNetworkSmoothingComplete to false until the first visual update, at which point if it remains true we will stop getting updates.
#rb Dan.Youhon
Change 2773599 on 2015/11/19 by Marc.Audy
Fix shadow variable, initialization order, and incorrect placement of ENGINE_API that fails compiles on clang
#codereview Aaron.McLeran
Change 2773661 on 2015/11/19 by Richard.Hinckley
#jira UEDOC-2685
Following up by deprecating the CastToPlayerController function for 4.11.
Change 2774707 on 2015/11/19 by Stan.Melax
FCollisionQueryParams constructor API pitfall
Deprecating something that will call the unintended constructor if the programmer provides a string literal instead of explicitly creating an FName.
So this line of code:
FCollisionQueryParams Param(FName(TEXT("DragDropTrace")));
will invoke:
FCollisionQueryParams::FCollisionQueryParams(FName,bool=false,...)
But this line of code:
FCollisionQueryParams Param(TEXT("DragDropTrace"));
invokes:
FCollisionQueryParams::FCollisionQueryParams(bool)
Yes that actually happens, the string literal (the TEXT("whatever")) ends up specifies a bool parameter instead of a FName parameter. So the name is lost and a flag (that is usually set to false) is now set to true (since the string literal address is non-NULL). Yeah, that's could potentially be not what the programmer had intended.
It looks like this interface was introduced in CL 1792949
back in august 2013. Since its been a couple of years, we'll phase it out using the deprecation approach.
Making an API change would break things for existing projects.
If it had been a more recent change we could have probably just gone down to a single constructor right away.
Figured out that going one step further and removing bool param's default and adding a default constructor would minimize the number of places that will be hit with this deprecation. So, yes this is *increasing* the number of construtors temporarily, but this solution has minimal impact on the community. Later after other to-be-deprecated constructor gets removed, the two remaining can be combined into one with defaults for all parameters.
see also
https://udn.unrealengine.com/questions/269512/dangerous-fcollisionqueryparams-overloads.html
#codereview ori.cohen
[CL 2775460 by Marc Audy in Main branch]
2015-11-20 11:02:37 -05:00
|
|
|
SetFilterFrequency();
|
2014-05-29 17:45:17 -04:00
|
|
|
|
2014-03-14 14:13:41 -04:00
|
|
|
FVector Location;
|
|
|
|
|
FVector Velocity;
|
|
|
|
|
|
|
|
|
|
// See file header for coordinate system explanation.
|
|
|
|
|
Location.X = WaveInstance->Location.X;
|
|
|
|
|
Location.Y = WaveInstance->Location.Z; // Z/Y swapped to match UE coordinate system
|
|
|
|
|
Location.Z = WaveInstance->Location.Y; // Z/Y swapped to match UE coordinate system
|
|
|
|
|
|
|
|
|
|
Velocity.X = WaveInstance->Velocity.X;
|
|
|
|
|
Velocity.Y = WaveInstance->Velocity.Z; // Z/Y swapped to match UE coordinate system
|
|
|
|
|
Velocity.Z = WaveInstance->Velocity.Y; // Z/Y swapped to match UE coordinate system
|
|
|
|
|
|
|
|
|
|
// We're using a relative coordinate system for un- spatialized sounds.
|
|
|
|
|
if( !WaveInstance->bUseSpatialization )
|
|
|
|
|
{
|
|
|
|
|
Location = FVector( 0.f, 0.f, 0.f );
|
|
|
|
|
}
|
|
|
|
|
|
2015-01-20 10:09:11 -05:00
|
|
|
// Set volume (Pitch changes are not supported on current Android platforms!)
|
2014-03-14 14:13:41 -04:00
|
|
|
// also Location & Velocity
|
2015-05-29 15:01:29 -04:00
|
|
|
|
2015-07-13 16:50:26 -04:00
|
|
|
// Avoid doing the log calculation each update by only doing it if the volume changed
|
|
|
|
|
if (Volume != VolumePreviousUpdate)
|
|
|
|
|
{
|
|
|
|
|
VolumePreviousUpdate = Volume;
|
|
|
|
|
static const int64 MinVolumeMillibel = -12000;
|
|
|
|
|
if (Volume > 0.0f)
|
|
|
|
|
{
|
|
|
|
|
// Convert volume to millibels.
|
|
|
|
|
SLmillibel MaxMillibel = 0;
|
|
|
|
|
(*SL_VolumeInterface)->GetMaxVolumeLevel(SL_VolumeInterface, &MaxMillibel);
|
|
|
|
|
SLmillibel VolumeMillibel = (SLmillibel)FMath::Clamp<int64>((int64)(2000.0f * log10f(Volume)), MinVolumeMillibel, (int64)MaxMillibel);
|
|
|
|
|
SLresult result = (*SL_VolumeInterface)->SetVolumeLevel(SL_VolumeInterface, VolumeMillibel);
|
|
|
|
|
check(SL_RESULT_SUCCESS == result);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
SLresult result = (*SL_VolumeInterface)->SetVolumeLevel(SL_VolumeInterface, MinVolumeMillibel);
|
|
|
|
|
check(SL_RESULT_SUCCESS == result);
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-05-04 17:36:45 -04:00
|
|
|
|
2014-03-14 14:13:41 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Plays the current wave instance.
|
|
|
|
|
*/
|
|
|
|
|
void FSLESSoundSource::Play( void )
|
|
|
|
|
{
|
|
|
|
|
if( WaveInstance )
|
|
|
|
|
{
|
2015-07-24 19:31:40 -04:00
|
|
|
// Reset the previous volume on play so it can be set at least once in the update function
|
|
|
|
|
VolumePreviousUpdate = -1.0f;
|
|
|
|
|
|
2015-12-10 16:56:55 -05:00
|
|
|
// Update volume now before starting play
|
|
|
|
|
Paused = false;
|
|
|
|
|
Update();
|
|
|
|
|
|
2014-03-14 14:13:41 -04:00
|
|
|
// set the player's state to playing
|
|
|
|
|
SLresult result = (*SL_PlayerPlayInterface)->SetPlayState(SL_PlayerPlayInterface, SL_PLAYSTATE_PLAYING);
|
|
|
|
|
check(SL_RESULT_SUCCESS == result);
|
|
|
|
|
|
|
|
|
|
Playing = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Stops the current wave instance and detaches it from the source.
|
|
|
|
|
*/
|
|
|
|
|
void FSLESSoundSource::Stop( void )
|
|
|
|
|
{
|
|
|
|
|
if( WaveInstance )
|
|
|
|
|
{
|
|
|
|
|
// set the player's state to stopped
|
|
|
|
|
SLresult result = (*SL_PlayerPlayInterface)->SetPlayState(SL_PlayerPlayInterface, SL_PLAYSTATE_STOPPED);
|
|
|
|
|
check(SL_RESULT_SUCCESS == result);
|
|
|
|
|
|
|
|
|
|
// Unregister looping callback
|
|
|
|
|
if( WaveInstance->LoopingMode != LOOP_Never )
|
|
|
|
|
{
|
|
|
|
|
result = (*SL_PlayerBufferQueue)->RegisterCallback(SL_PlayerBufferQueue, NULL, NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DestroyPlayer();
|
|
|
|
|
ReleaseResources();
|
|
|
|
|
|
|
|
|
|
Paused = false;
|
|
|
|
|
Playing = false;
|
|
|
|
|
Buffer = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FSoundSource::Stop();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Pauses playback of current wave instance.
|
|
|
|
|
*/
|
|
|
|
|
void FSLESSoundSource::Pause( void )
|
|
|
|
|
{
|
|
|
|
|
if( WaveInstance )
|
|
|
|
|
{
|
|
|
|
|
Paused = true;
|
|
|
|
|
|
|
|
|
|
// set the player's state to paused
|
|
|
|
|
SLresult result = (*SL_PlayerPlayInterface)->SetPlayState(SL_PlayerPlayInterface, SL_PLAYSTATE_PAUSED);
|
|
|
|
|
check(SL_RESULT_SUCCESS == result);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns TRUE if the source has finished playing
|
|
|
|
|
*/
|
|
|
|
|
bool FSLESSoundSource::IsSourceFinished( void )
|
|
|
|
|
{
|
|
|
|
|
SLuint32 PlayState;
|
|
|
|
|
|
|
|
|
|
// set the player's state to playing
|
|
|
|
|
SLresult result = (*SL_PlayerPlayInterface)->GetPlayState(SL_PlayerPlayInterface, &PlayState);
|
|
|
|
|
check(SL_RESULT_SUCCESS == result);
|
|
|
|
|
|
|
|
|
|
if( PlayState == SL_PLAYSTATE_STOPPED )
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 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 FSLESSoundSource::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
|
|
|
|
|
{
|
|
|
|
|
if (bHasLooped)
|
|
|
|
|
{
|
|
|
|
|
switch (WaveInstance->LoopingMode)
|
|
|
|
|
{
|
|
|
|
|
case LOOP_Forever:
|
|
|
|
|
bHasLooped = false;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case LOOP_Never:
|
|
|
|
|
bBuffersToFlush = true;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case LOOP_WithNotification:
|
|
|
|
|
bHasLooped = false;
|
|
|
|
|
// Notify the wave instance that it has finished playing.
|
|
|
|
|
WaveInstance->NotifyFinished();
|
|
|
|
|
break;
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|