You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
#lockdown Nick.Penwarden
#rb none
==========================
MAJOR FEATURES + CHANGES
==========================
Change 3026802 on 2016/06/24 by Lina.Halper
#Pose Asset work
# additive blending change : additive scale is saved to [targetscale/sourcescale - 1] where it used to be [targetscale/sourcescale] since blending doesn't work with it
- Blending should work once we save to [targetscale/sourcescale - 1] as normal - i.e. if you blend 0.3, it should not shrink the mesh because you applyed additive to 0.3
- When apply the scale to base, it should multiply [additive scale + 1 ] where additive scale is [targetscale/sourcescale - 1]
- Changed FTransform::Blend to FTransform::Lerp since it's literally just Lerp. Name Blend should be used for Accumulate but changing the name now is dangerous, so I'm keeping Accumulate but changed Blend to Lerp
# pose asset preview fix
- made sure it adds to curve, so you don't have to use delegate to apply
- PreviewOverride is now added to output curve, so we don't have to apply that curve later
- only reason of anim instance delegate is now for normal anim blueprint.
#pose asset change
- Curve extraction happens with ExtractContext, the output curve is stricly output curve
- Pose Asset supports Shrink now, but only shrink if full pose
- Added PoseHandler to handle most of common stuff between different pose nodes
- Still have to work on how to update pose asset - wip
- todo: clean up single node player to handle pose asset in a function
#code review:Martin.Wilson, James.Golding
Change 3027519 on 2016/06/24 by Lina.Halper
Reverted FTransform name change as that causes compile errors due to lack of deprecated messages
- not worth to keep the old functions and add new one
#code review: Martin.Wilson
Change 3060205 on 2016/07/21 by Zak.Middleton
#ue4 - Don't strip map/package name from shipping on dedicated server in UGameInstance::StartGameInstance(), to allow specifying map name on servers via commandline. Don't ever parse "-replay" on dedicated servers.
#jira UE-29424
github #2273
Change 3061365 on 2016/07/22 by Jon.Nabozny
Fix single frame character flip on death in ShooterGame.
#jira UE-28053
Change 3061370 on 2016/07/22 by Jon.Nabozny
Fix PhysX error where CCD is enabled on a Kinematic body.
#jira UE-33463
Change 3061406 on 2016/07/22 by Lukasz.Furman
deprecated blueprint interface for avoidance groups, added new set of functions working with FNavAvoidanceMask struct instead of packed flags
#jira UE-32625
Change 3061847 on 2016/07/22 by Bob.Tellez
Duplicating CL#3058203 from //Fortnite/Main
#UE4 Clearing pin links for fixed up BT nodes so they are not asymmetrical by the time the node gets destroyed, causing an ensure to fail. Also removing the replaced nodes so they are no longer saved in the package.
Change 3063080 on 2016/07/25 by Benn.Gallagher
Fixes to APEX rendering artefacts, by discarding all incoming render data from the APEX asset and skinning our render data to the simulation mesh.
#jira UEFW-182
Change 3063119 on 2016/07/25 by Ori.Cohen
Fix constraint index not being assigned correctly. Also expose FindConstraintBoneName to blueprints
#JIRA UE-33716
Change 3063956 on 2016/07/25 by Ori.Cohen
Fix SetRootBodyIndex so that it uses world space transform instead of ref skeleton. Fixes a few issues when using ragdolls on skeletal mesh assets that were imported with offset/rotation.
Also fix crash when opening up old physics asset and changing its mesh.
#JIRA UE-33753
#JIRA UE-33754
Change 3064846 on 2016/07/26 by Benn.Gallagher
Fixed crash when loading skeletal meshes with clothing in -game
#jira UE-33771
Change 3065237 on 2016/07/26 by Ori.Cohen
Fix physics handle component not accounting for bone rotation/offset
Change 3065241 on 2016/07/26 by Ori.Cohen
Fix constraint component not using root body when no bone name is provided.
Change 3069508 on 2016/07/28 by Aaron.McLeran
Adding debug exec commands to help with debugging audio
Solos sounds when using the following exec commands:
- AudioSoloSoundWave
Solos sounds with the given wave name
- AudioSoloSoundClass
Solos sounds with the given sound class
- AudioSoloSoundCue
Solos sounds with the given sound cue name
-All solo commands can be active at the same time and will solo sounds that match all their substring name searches.
- Only active in non-shipping builds
Change 3070857 on 2016/07/29 by Ori.Cohen
Expose Grab Rotation for physics handle component. Also brought over changes from Jeff.F where we can now specify hard vs soft physics handle
Change 3072817 on 2016/08/01 by Wes.Hunt
turn off optimizations to speed up build times. ~45s->~5sec apiece.
Change 3073855 on 2016/08/02 by Zak.Middleton
#ue4 - Fix tooltip text for p.NetEnableListenServerSmoothing command.
Change 3074325 on 2016/08/02 by Aaron.McLeran
UE-34081 Procedural Sound Wave Fails to Play when returning 0 bytes in GeneratePCMData callback
- Returning 0 bytes in GeneratePCMData results in the procedural sound wave not continuing to play audio. Instead of returning 0, this change returns an empty buffer if the procedural sound wave doesn't have audio ready to generate (due to loading or some other issue).
- Change also fixes a threading issue with QueueAudio queing audio on game thread but being consumed by audio device thread.
- Implementing 3003851 from UT into Dev-Framework.
Change 3075259 on 2016/08/03 by James.Golding
Fix up my ProcMeshComp test BPs
Change 3076637 on 2016/08/03 by Aaron.McLeran
UE-34081 Adjustments to procedural sound wave implementation
- Cleaned up base class implementation of GeneratePCMData
- Calls bound callback function first if there aren't enough samples available, then pumps the enqueued audio buffers
- Wrote a prototype gameplay C++ class that implements this to test procedural sound wave generation with a sine tone and to test not immediately returning audio when called back (to test the empty buffer copy code).
Change 3077340 on 2016/08/04 by Marc.Audy
Minor header cleanups and readability changes
Change 3079669 on 2016/08/05 by Aaron.McLeran
OR-26580 Implementing 3071258 to Dev-Framework
Change 3080958 on 2016/08/08 by Aaron.McLeran
UE-34325 In process audio resource is corrupted during level change.
- When a sound buffer is flushed from audio device manager and tries to stop sounds using a resource, was possible for the async header parse task to be in-flight, which would cause a crash
- Fix is to bring back the code to call EnsureCompletion on tasks in the FreeResoruces function of the sound source object. This will potentially encure a slight perf increase when stopping a sound but audio engine is now going to run on a separate thread, so shouldn't have a game-thread impact in non-editor builds.
#tests ran repro case described in bug several times without crashing (was previous 100% repro)
Change 3081314 on 2016/08/08 by Marc.Audy
Trim DSO components that no longer have an archetype in the class hierarchy
#jira UE-28374
Change 3082356 on 2016/08/09 by Marc.Audy
Always clear lifespan timers on EndPlay
Change 3082680 on 2016/08/09 by Lukasz.Furman
fixed gameplay debugger extensions activating during simulate in editor
#jira UE-33343
Change 3082731 on 2016/08/09 by Aaron.McLeran
UE-30629 "FXAudio2SoundSource contains NaN in channel" occur in test ShooterGame build
- Issue was inverse transform was uninitialized in first update tick of the audio device. If a sound is trying to play in the first update tick, it'd generate NaNs as it tries to transform sound positions relative to an unitialized inverse listener transform
- Fix is to update listener transform BEFORE updating sounds.
- Also added an initialization to the inverse listener transform for good measure
Change 3082732 on 2016/08/09 by Aaron.McLeran
Removing log spam on local player node
Change 3083051 on 2016/08/09 by Marc.Audy
FLocalPlayerContext functions no longer check by default
#jira UE-33370
Change 3083271 on 2016/08/09 by Dan.Reynolds
Checking in QA test map for procedural sound waves
-Added code for a UQAProceudralSoundWave that generates a sine tone that can be set by hz or midi note
- Added BP code which uses the procedural sound wave and does some melody generation.
Change 3083947 on 2016/08/10 by Lukasz.Furman
fixed navigation modifier export for capsule components
#ue4
Change 3084594 on 2016/08/10 by Marc.Audy
Enable threaded audio again
Change 3086237 on 2016/08/11 by Marc.Audy
PR #2579: New feature: client-side level streaming (Contributed by slonopotamus)
#jira UE-32896
Change 3086544 on 2016/08/11 by Michael.Noland
Paper2D: Fixed a crash when mutating grouped sprite components at runtime, and cleaned up how collision rendering is done for grouped sprite components addressing some other issues as well
#jira UE-34223
#tests Tested with repro project from JIRA, as well as adding/removing instances in the editor and with some sprites in the group having collision and others not, clearing collision on the component itself, etc...
Change 3087280 on 2016/08/12 by Marc.Audy
Fatal error including the path if a physics module fails to load
Change 3088105 on 2016/08/12 by Marc.Audy
Strip native components that no longer exist from BP CDOs as well.
#jira UE-28374
[CL 3090563 by Marc Audy in Main branch]
605 lines
18 KiB
C++
605 lines
18 KiB
C++
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
|
|
|
|
/*------------------------------------------------------------------------------------
|
|
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
|
|
{
|
|
// Enqueue the previously decoded buffer
|
|
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;
|
|
}
|
|
|
|
// Sound decoding is complete, just waiting to finish playing
|
|
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;
|
|
}
|
|
|
|
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)"));
|
|
}
|
|
|
|
// Switch to the next buffer and decode for the next time the callback fires if we didn't just get the last buffer
|
|
BufferInUse = !BufferInUse;
|
|
if (bHasLooped == false || WaveInstance->LoopingMode != LOOP_Never)
|
|
{
|
|
// Do this in the callback thread instead of creating an asynchronous task (thread id from callback is not consistent and use of TLS for stats causes issues)
|
|
if (ReadMorePCMData(BufferInUse, EDataReadMode::Synchronous))
|
|
{
|
|
// If this is a synchronous source we may get notified immediately that we have looped
|
|
bHasLooped = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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)
|
|
{
|
|
UE_LOG(LogAndroidAudio, Warning, TEXT("FAILED OPENSL BUFFER CreateAudioPlayer 0x%x"), result);
|
|
return false;
|
|
}
|
|
|
|
bool bFailedSetup = false;
|
|
|
|
// realize the player
|
|
result = (*SL_PlayerObject)->Realize(SL_PlayerObject, SL_BOOLEAN_FALSE);
|
|
if (result != SL_RESULT_SUCCESS) { UE_LOG(LogAndroidAudio, Warning, TEXT("FAILED OPENSL BUFFER Realize 0x%x"), result); return false; }
|
|
|
|
// get the play interface
|
|
result = (*SL_PlayerObject)->GetInterface(SL_PlayerObject, SL_IID_PLAY, &SL_PlayerPlayInterface);
|
|
if (result != SL_RESULT_SUCCESS) { UE_LOG(LogAndroidAudio, Warning, TEXT("FAILED OPENSL BUFFER GetInterface SL_IID_PLAY 0x%x"), result); bFailedSetup |= true; }
|
|
// volume
|
|
result = (*SL_PlayerObject)->GetInterface(SL_PlayerObject, SL_IID_VOLUME, &SL_VolumeInterface);
|
|
if (result != SL_RESULT_SUCCESS) { UE_LOG(LogAndroidAudio, Warning, TEXT("FAILED OPENSL BUFFER GetInterface SL_IID_VOLUME 0x%x"), result); bFailedSetup |= true; }
|
|
// buffer system
|
|
result = (*SL_PlayerObject)->GetInterface(SL_PlayerObject, SL_IID_BUFFERQUEUE, &SL_PlayerBufferQueue);
|
|
if (result != SL_RESULT_SUCCESS) { UE_LOG(LogAndroidAudio, Warning, TEXT("FAILED OPENSL BUFFER GetInterface SL_IID_BUFFERQUEUE 0x%x"), result); bFailedSetup |= true; }
|
|
|
|
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);
|
|
if (result != SL_RESULT_SUCCESS) { UE_LOG(LogAndroidAudio, Warning, TEXT("FAILED OPENSL BUFFER QUEUE RegisterCallback 0x%x "), result); return false; }
|
|
}
|
|
|
|
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 0x%x params( %p, %d)"), result, Buffer->AudioData, int32(Buffer->GetSize()));
|
|
if (bLoop)
|
|
{
|
|
result = (*SL_PlayerBufferQueue)->RegisterCallback(SL_PlayerBufferQueue, NULL, NULL);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bStreamedSound = false;
|
|
bHasLooped = false;
|
|
bBuffersToFlush = false;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool FSLESSoundSource::ReadMorePCMData(const int32 BufferIndex, EDataReadMode DataReadMode)
|
|
{
|
|
USoundWave* WaveData = WaveInstance->WaveData;
|
|
if (WaveData && WaveData->bProcedural)
|
|
{
|
|
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;
|
|
}
|
|
else
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
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
|
|
BufferSize = Buffer->GetRTBufferSize() * Buffer->NumChannels;
|
|
|
|
AudioBuffers[0].AudioData = (uint8*)FMemory::Malloc(BufferSize);
|
|
AudioBuffers[0].AudioDataSize = BufferSize;
|
|
|
|
AudioBuffers[1].AudioData = (uint8*)FMemory::Malloc(BufferSize);
|
|
AudioBuffers[1].AudioDataSize = BufferSize;
|
|
|
|
// Only use the cached data if we're starting from the beginning, otherwise we'll have to take a synchronous hit
|
|
if (WaveInstance->WaveData && WaveInstance->WaveData->CachedRealtimeFirstBuffer && WaveInstance->StartTime == 0.f)
|
|
{
|
|
FMemory::Memcpy((uint8*)AudioBuffers[0].AudioData, WaveInstance->WaveData->CachedRealtimeFirstBuffer, BufferSize);
|
|
ReadMorePCMData(1, EDataReadMode::AsynchronousSkipFirstFrame);
|
|
}
|
|
else
|
|
{
|
|
ReadMorePCMData(0, EDataReadMode::Synchronous);
|
|
ReadMorePCMData(1, EDataReadMode::Asynchronous);
|
|
}
|
|
|
|
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 );
|
|
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; }
|
|
}
|
|
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.
|
|
*
|
|
* @param WaveInstance wave instance being primed for playback
|
|
* @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())
|
|
{
|
|
WaveInstance = InWaveInstance;
|
|
|
|
if (WaveInstance->StartTime > 0.f)
|
|
{
|
|
Buffer->Seek(WaveInstance->StartTime);
|
|
}
|
|
|
|
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),
|
|
BufferSize(0),
|
|
BufferInUse(0),
|
|
VolumePreviousUpdate(-1.0f),
|
|
bHasLooped(false),
|
|
SL_PlayerObject(NULL),
|
|
SL_PlayerPlayInterface(NULL),
|
|
SL_PlayerBufferQueue(NULL),
|
|
SL_VolumeInterface(NULL),
|
|
RealtimeAsyncTask(NULL)
|
|
{
|
|
FMemory::Memzero( AudioBuffers, sizeof( AudioBuffers ) );
|
|
}
|
|
|
|
/**
|
|
* Clean up any hardware referenced by the sound source
|
|
*/
|
|
FSLESSoundSource::~FSLESSoundSource( void )
|
|
{
|
|
DestroyPlayer();
|
|
|
|
ReleaseResources();
|
|
}
|
|
|
|
void FSLESSoundSource::ReleaseResources()
|
|
{
|
|
if (RealtimeAsyncTask)
|
|
{
|
|
RealtimeAsyncTask->EnsureCompletion();
|
|
delete RealtimeAsyncTask;
|
|
RealtimeAsyncTask = nullptr;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
float Volume = WaveInstance->Volume * WaveInstance->VolumeMultiplier;
|
|
if (SetStereoBleed())
|
|
{
|
|
// Emulate the bleed to rear speakers followed by stereo fold down
|
|
Volume *= 1.25f;
|
|
}
|
|
Volume *= AudioDevice->GetPlatformAudioHeadroom();
|
|
Volume = FMath::Clamp(Volume, 0.0f, MAX_VOLUME);
|
|
|
|
Volume = FSoundSource::GetDebugVolume(Volume);
|
|
|
|
const float Pitch = FMath::Clamp<float>(WaveInstance->Pitch, MIN_PITCH, MAX_PITCH);
|
|
|
|
// Set whether to apply reverb
|
|
SetReverbApplied(true);
|
|
|
|
SetFilterFrequency();
|
|
|
|
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 );
|
|
}
|
|
|
|
// Set volume (Pitch changes are not supported on current Android platforms!)
|
|
// also Location & Velocity
|
|
|
|
// 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);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Plays the current wave instance.
|
|
*/
|
|
void FSLESSoundSource::Play( void )
|
|
{
|
|
if( WaveInstance )
|
|
{
|
|
// Reset the previous volume on play so it can be set at least once in the update function
|
|
VolumePreviousUpdate = -1.0f;
|
|
|
|
// Update volume now before starting play
|
|
Paused = false;
|
|
Update();
|
|
|
|
// 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;
|
|
}
|