Files
UnrealEngineUWP/Engine/Source/Runtime/Android/AndroidAudio/Private/AndroidAudioSource.cpp
Thomas Sarkanen 67f958285a Copying //UE4/Dev-AnimPhys to //UE4/Dev-Main (Source: //UE4/Dev-AnimPhys @ 3436999)
#lockdown Nick.Penwarden

==========================
MAJOR FEATURES + CHANGES
==========================

Change 3354003 on 2017/03/20 by Thomas.Sarkanen

	Back out changelist 3353914

Change 3355932 on 2017/03/21 by Thomas.Sarkanen

	Back out changelist 3354003

	Reinstating merge from Main:

	Merging //UE4/Dev-Main to Dev-AnimPhys (//UE4/Dev-AnimPhys) @ CL 3353839

Change 3385512 on 2017/04/07 by Aaron.McLeran

	Bringing changes over from FN that fix audio streaming on PC/Mac/XboxOne/PS4

	CL#3318457 - Fix crash when recycling PS4 sound sources.
	CL#3313213 - Allowing XboxOne to cook streaming audio
	CL#3313719 - GetWaveFormat now returns OPUS for streaming audio waves
	CL#3320066 - Added libopus for XboxOne
	CL#3320070 - libopus is now properly linked in XboxOne

	CL#3313219 - Allowing Mac to cook streaming audio
	CL#3315332 - Fixed audio streaming on Mac
	CL#3315335 - (additional file missed in previous CL)

	CL#3313207 - Sounds now register themselves with the audio streaming manager even if they are loaded before the audio device manager is created.
	CL#3313294 - Removed some accidental debugging code that was mistakenly added in CL#3313207
	CL#3318530 - Fix threading issues in FAudioStreamingManager

	CL#3340718 - Fix for crash with audio streaming
	CL#3340844 - Fix for more thread safety in audio streaming manager
	CL#3343794 - Added a check in destructor of loaded chunk
	CL#3343794 - Removing check in stopping a source
	CL#3355393 - Moving audio streaming callbacks to use indices rather than ptrs to elements in dynamic array
	CL#3369020 - bumping up size of compressed chunks for AT9 files when doing stream chunk compression
	CL#3369131 - bumping up AT9 version number to get new AT9 cooks for larger chunks

	CL#3373626 - Fixing ps4 streaming
	CL#3375110 - Reverting some changes from 3373626
	CL#3382078 - Making audio streaming decoding async to audio thread for xaudio2
	CL#3383214 - Fixing buffer order issue for audio streaming

Change 3386321 on 2017/04/10 by Lina.Halper

	#ANIM : preview
	- Attache preview mesh to use copy mesh pose

	#jira: UE-43114, UEAP-186
	#rb: Thomas.Sarkanen

Change 3386415 on 2017/04/10 by Ori.Cohen

	Improve the cost of UpdateKinematicBodies - added the ability to defer non simulating bodies.

	#JIRA UEAP-79

Change 3386418 on 2017/04/10 by Ori.Cohen

	Fix physx memory leak when a commandlet loads many assets without ticking scene

	#JIRA UE-43378

Change 3386569 on 2017/04/10 by dan.reynolds

	Updated dummy platform generated by standalone AEOverview tests to distinguish floor materials between the platform and the test zone.

Change 3386714 on 2017/04/10 by Ori.Cohen

	Improve stats extensibility and expose it to the automation framework.

Change 3386805 on 2017/04/10 by Lina.Halper

	Fix build error for editor

	#rb: none

Change 3386854 on 2017/04/10 by Lina.Halper

	build fix for clang

	#rb:none

Change 3387198 on 2017/04/10 by Aaron.McLeran

	#jira UE-43699 Deleting unused velocity variable.

	OpenAL's velocity is not supported in WebAudio.

	Removing dead code in AndroidAudioSource.cpp

Change 3387346 on 2017/04/10 by Ori.Cohen

	Added performance regression map for physics (update kinematic bones and postBroadPhase)

	#JIRA UEAP-79

Change 3387409 on 2017/04/10 by Ori.Cohen

	Fix build, forgot to update this code

Change 3387536 on 2017/04/10 by Lina.Halper

	Merging using AnimPhys-Fortnite-Main
	- fix preview mesh selection/animation

	#code review: Thomas.Sarkanen
	#rb: none

	#i_need_autocorrect

Change 3387995 on 2017/04/11 by Martin.Wilson

	Live link updates
	- Refactor of provider api (separate update of hierarchy and transforms)
	- multi connection streaming from provider
	- provider maintains internal state so that new connections can be updated without interaction with streaming source.
	- Lifetime changes (connection timeout)

Change 3388241 on 2017/04/11 by Lina.Halper

	Merging using AnimPhys-Fortnite-Main

	- merge CL of 3388238
	#rb: Thomas.Sarkanen

Change 3388294 on 2017/04/11 by Lina.Halper

	build fix

	#rb: none

Change 3388341 on 2017/04/11 by Ori.Cohen

	Turn off vs2013 for physx

Change 3389115 on 2017/04/11 by Ori.Cohen

	Forgot missing blueprint for perf test

Change 3389224 on 2017/04/11 by Ori.Cohen

	Added sweep multi tests to perf regression

	#JIRA UEAP-79

Change 3389984 on 2017/04/12 by Martin.Wilson

	CIS Fix

Change 3390315 on 2017/04/12 by Lina.Halper

	- fix on crash of component array when shutting down anim blueprint

	#jira: UE-43868
	#rb: Thomas.Sarkanen

Change 3390402 on 2017/04/12 by Martin.Wilson

	Fix update not being called on post process instances when the main anim instance does not do a parallel update

	#jira UE-43906

Change 3390772 on 2017/04/12 by Lina.Halper

	Fix crash on importing LOD with lesser # of joints

	#rb: Benn.Gallagher

Change 3394850 on 2017/04/14 by Aaron.McLeran

	Adjusting how wavetable generation works for custom wavetables.

	- Changed wavetable creation to use a TSharedPtr vs a raw ptr.

Change 3394853 on 2017/04/14 by Aaron.McLeran

	Bringing from Odin the ability to set the lowpass filter frequency on an audio component from BP

Change 3395684 on 2017/04/17 by Ori.Cohen

	Make debugdraw for line traces const correct.

Change 3396680 on 2017/04/17 by Ori.Cohen

	Added a total scene query stat and the ability to trace all scene queries

Change 3397564 on 2017/04/18 by Benn.Gallagher

	Added clothing functional and performance test map + assets.

Change 3397769 on 2017/04/18 by Thomas.Sarkanen

	CIS fix

	Fixup incorrect AudioStreaming.cpp merge when bringing Main into Dev-AnimPhys

Change 3398518 on 2017/04/18 by Lina.Halper

	Mirroring fix on set world rotation

	#rb: Zak.Middleton
	#jira: UE-43830

Change 3400400 on 2017/04/19 by Chad.Garyet

	adding switch physx build to anim-phys

Change 3400416 on 2017/04/19 by Chad.Garyet

	updated email targets to include switch

Change 3402005 on 2017/04/20 by Ori.Cohen

	Pass stats into scene queries. Not all call sites are updated yet, waiting on Jon for uber search/replace script.

Change 3402264 on 2017/04/20 by Ori.Cohen

	CIS fix

Change 3402344 on 2017/04/20 by Ori.Cohen

	Turn off find unknown (was on by mistake)

Change 3403311 on 2017/04/21 by Benn.Gallagher

	Clothing changes from Dev-General. Fixed LOD pops, mesh swap crashes and convex collision locations

Change 3403399 on 2017/04/21 by Benn.Gallagher

	Lighting build, content cleanup and reorganization for clothing test map

Change 3403401 on 2017/04/21 by Benn.Gallagher

	Clothing test ground truth updates after lighting build.

Change 3403813 on 2017/04/21 by danny.bouimad

	Adding everything needed for our multiplat map TM-AnimPhys

Change 3403900 on 2017/04/21 by mason.seay

	Added WIP text to tests that need fixup

Change 3405383 on 2017/04/24 by Ori.Cohen

	Fix typo where complex flag was not being passed in  to constructor.

	#JIRA UE-44278, UE-44279

Change 3405389 on 2017/04/24 by Martin.Wilson

	Live link:
	- Added support for sending curve data across live link and applying it via the Live Link node
	- Added pose snapshots which are built in the live link clients tick and read by the rest of the engine, save reading live data.

Change 3405569 on 2017/04/24 by Martin.Wilson

	Missed file from CL 3405389

Change 3405810 on 2017/04/24 by Chad.Garyet

	fixing busted target for dev-animphys stream

Change 3406566 on 2017/04/24 by Aaron.McLeran

	#jira UE-44272 Fixing granular synth with packaged builds

	- Changed the way granular synth component and wave table component get PCM data from USoundWave assets. No duplication, just precache directly.

Change 3406694 on 2017/04/24 by Aaron.McLeran

	Update to phonon/steam audio plugin from valve

Change 3407794 on 2017/04/25 by Aaron.McLeran

	#jira UE-44357 Fix for attenuation settings in sequencer

Change 3407848 on 2017/04/25 by Jon.Nabozny

	Add stats to FCollisionQueryParams (continued from CL-3402005).

Change 3407857 on 2017/04/25 by Jon.Nabozny

	Disable FIND_UNKNOWN_SCENE_QUERIES.

Change 3407915 on 2017/04/25 by Lina.Halper

	Animation Automation Test for curve and simple notify

Change 3408164 on 2017/04/25 by Ori.Cohen

	Expose the physx tree rebuild rate.

Change 3408174 on 2017/04/25 by Lina.Halper

	- Changed 1, 2, 3, 4 for ordering of timing
	- Made sure the notify test takes more time between shots.

Change 3408359 on 2017/04/25 by Jon.Nabozny

	Fix FConfigFile::Write for arrays of different sizes.
	(Looks like it is still broke for arrays of the same same, with different values).

Change 3408633 on 2017/04/25 by Aaron.McLeran

	#jira UE-44297 Fix for animating sound cue graph when editor "non-realtime" button is checked

	- Fix is to explicitely register an active timer lambda that queries the preview audio component while the sound cue is playing

Change 3408768 on 2017/04/25 by Aaron.McLeran

	Fixing UHT crash

Change 3409225 on 2017/04/26 by Lina.Halper

	Increase tolerance for the shot test. It's very sensitive otherwise.

Change 3409313 on 2017/04/26 by Benn.Gallagher

	Refactor of clothing paint tool framework to create a more extensible editor and get rid of some GDC techdebt

Change 3409478 on 2017/04/26 by danny.bouimad

	Moved Text Actor forwards as it was causing zFighting

Change 3409572 on 2017/04/26 by Benn.Gallagher

	CIS fix after cloth painter changes

Change 3409585 on 2017/04/26 by danny.bouimad

	Updated Tm-AnimPhys to utilize the AEOverview maps, also found a crash with viewing shader complexity that only occurs on this map.

Change 3410948 on 2017/04/27 by Martin.Wilson

	Live Link:
	- Add subject clearing support to client / message bus protocol
	- Update ref skeleton when subject changes.
	- Remove old classes

Change 3413305 on 2017/04/28 by Danny.Bouimad

	Disabled audio tests on AnimPhys Testmap to hopefuly stop the lighting crashes during launch on (content problem)

Change 3413408 on 2017/04/28 by mason.seay

	Resaving to clear empty engine version warnings

Change 3413418 on 2017/04/28 by Benn.Gallagher

	CIS fix, #pragma once in wrong place (after an include)

Change 3413737 on 2017/04/28 by Martin.Wilson

	Rename Live Link Message Bus messages to contain the word message to avoid future name clashes

Change 3414121 on 2017/04/28 by Ori.Cohen

	Added task batching for physx tasks. Set fortnite to 8 as we already have a lot of thread contention during that time and it's best to just do it all in a single task.

Change 3417833 on 2017/05/02 by Thomas.Sarkanen

	Fix bad merge in SynthComponentGranulator.cpp

Change 3418174 on 2017/05/02 by Jon.Nabozny

	Fix memory leak in UDestructibleComponent::SetSkeletalMesh

Change 3418907 on 2017/05/02 by Aaron.McLeran

	#jira UE-44599 Fixing active sound un-pause issue.

	- While paused, active sounds were updating their active playbacktime.

Change 3419001 on 2017/05/02 by Ori.Cohen

	Added GetNumSimulatedAndAwake so that we can easily test for jitter.

Change 3419079 on 2017/05/02 by Ori.Cohen

	Added a jitter automated test.

Change 3419213 on 2017/05/02 by mason.seay

	Reaving content to remove empty engine version warnings

Change 3419351 on 2017/05/02 by Ori.Cohen

	Added automated test for raycasting against landscape from underneath (JIRA UE-39819)
	It looks like this is currently broken

Change 3419356 on 2017/05/02 by Ori.Cohen

	Updated test with associated JIRA where we first saw this

Change 3419478 on 2017/05/02 by Ori.Cohen

	Added automated test for origin shift regression crash when using aggregates.

Change 3420736 on 2017/05/03 by Ori.Cohen

	Added automated test for moving objects during an overlap callback for UE-41450
	#rnx

Change 3420803 on 2017/05/03 by Ori.Cohen

	Added automated test for JIRA UE-18019
	#rnx

Change 3420835 on 2017/05/03 by Jurre.deBaare

	Anim modifier BP for release notes

Change 3421185 on 2017/05/03 by Ori.Cohen

	Missing file

Change 3422673 on 2017/05/04 by danny.bouimad

	Fixed the cooked/uncooked lighting issue with AEO_StageFloor. The lights should no longer repeatidly spawn when loading in as sub levels.

Change 3422898 on 2017/05/04 by Danny.Bouimad

	Updating QA Audio Content

Change 3422908 on 2017/05/04 by Danny.Bouimad

	Fixing Automation CIS error 'Can't find file for asset. /Game/Tests/Physics/ISMCStaticSweep_BuiltData'

Change 3423508 on 2017/05/04 by Danny.Bouimad

	Replacing ground truth and adding build data for nonissue Automation CIS failure OverlapCallback

Change 3423634 on 2017/05/04 by danny.bouimad

	Made updates to TM-AnimPhys testmap

Change 3423870 on 2017/05/04 by Ori.Cohen

	Fix wheels separating from vehicle due to world kinematic refactor. Added temp variable for now

	#jira UE-44624

Change 3423917 on 2017/05/04 by Ori.Cohen

	Assert_Equal for int returns a bool

Change 3425267 on 2017/05/05 by Martin.Wilson

	Live Link

	- Add interpolation to subjects
	- Add connection settings that can be modified in client panel. All subjects modified by a connection use its connection settings
	- Give live link sources their client Guid so that they can send it with subject data

Change 3425303 on 2017/05/05 by Martin.Wilson

	Missed file from CL 3425267

Change 3430351 on 2017/05/09 by Martin.Wilson

	Crash fix for live link interpolation

Change 3430601 on 2017/05/09 by Benn.Gallagher

	Disabled clothing perf test temporarily due to stats issues

Change 3432669 on 2017/05/10 by Ori.Cohen

	Temporarily turn off line trace under heightfield test. This is a known bug which won't be fixed until 4.17

Change 3432679 on 2017/05/10 by Ori.Cohen

	Temporarily turn off check during TLS release on Switch.

Change 3434960 on 2017/05/11 by danny.bouimad

	Disabled content on TM-AnimPhys that was casuing a out of memory when drawing debug lines on switch.

Change 3436639 on 2017/05/12 by Danny.Bouimad

	Updating ground truths and map for OverlapCallBack to fix CIS error.

[CL 3437043 by Thomas Sarkanen in Main branch]
2017-05-12 11:21:11 -04:00

621 lines
18 KiB
C++

// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved.
/*------------------------------------------------------------------------------------
FSLESSoundSource.
------------------------------------------------------------------------------------*/
#include "AndroidAudioDevice.h"
#include "AudioDecompress.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, SLESBuffer->AudioData, SLESBuffer->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(SLESBuffer->NumChannels), SLuint32( SLESBuffer->SampleRate * 1000 ),
SL_PCMSAMPLEFORMAT_FIXED_16, SL_PCMSAMPLEFORMAT_FIXED_16,
SLESBuffer->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, SLESBuffer->AudioData, SLESBuffer->GetSize() );
if (result != SL_RESULT_SUCCESS) {
UE_LOG(LogAndroidAudio, Warning, TEXT("FAILED OPENSL BUFFER Enqueue SL_PlayerBufferQueue 0x%x params( %p, %d)"), result, SLESBuffer->AudioData, int32(SLESBuffer->GetSize()));
if (bLoop)
{
result = (*SL_PlayerBufferQueue)->RegisterCallback(SL_PlayerBufferQueue, NULL, NULL);
}
return false;
}
bStreamedSound = false;
bHasLooped = false;
bHasPositionUpdated = 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 SLESBuffer->ReadCompressedData(AudioBuffers[BufferIndex].AudioData, WaveInstance->LoopingMode != LOOP_Never);
}
else
{
RealtimeAsyncTask = new FAsyncRealtimeAudioTask(SLESBuffer, 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 = SLESBuffer->GetRTBufferSize() * SLESBuffer->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, SLESBuffer->AudioData, int32(SLESBuffer->GetSize())); return false; }
}
else
{
return false;
}
bStreamedSound = true;
bHasLooped = false;
bBuffersToFlush = false;
bHasPositionUpdated = 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 )
{
FSoundSource::InitCommon();
// 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 (SLESBuffer && SLESBuffer->ResourceID == 0)
{
UE_LOG( LogAndroidAudio, Warning, TEXT(" InitSoundSouce with Buffer already allocated"));
delete SLESBuffer;
SLESBuffer = nullptr;
Buffer = nullptr;
}
if (SL_PlayerObject)
{
UE_LOG( LogAndroidAudio, Warning, TEXT(" InitSoundSouce with PlayerObject not NULL, possible leak"));
}
// Find matching buffer.
SLESBuffer = FSLESSoundBuffer::Init( (FSLESAudioDevice *)AudioDevice, InWaveInstance->WaveData );
Buffer = SLESBuffer;
if( SLESBuffer && 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)
{
SLESBuffer->Seek(WaveInstance->StartTime);
}
switch( SLESBuffer->Format)
{
case SoundFormat_PCM:
bFailedSetup |= !EnqueuePCMBuffer( InWaveInstance->LoopingMode != LOOP_Never );
break;
case SoundFormat_PCMRT:
case SoundFormat_Streaming:
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 (SLESBuffer && SLESBuffer->ResourceID == 0)
{
delete SLESBuffer;
SLESBuffer = nullptr;
Buffer = nullptr;
}
}
return false;
}
FSLESSoundSource::FSLESSoundSource( class FAudioDevice* InAudioDevice )
: FSoundSource( InAudioDevice ),
Device((FSLESAudioDevice *)InAudioDevice),
SLESBuffer( NULL ),
bStreamedSound(false),
bBuffersToFlush(false),
BufferSize(0),
BufferInUse(0),
VolumePreviousUpdate(-1.0f),
bHasLooped(false),
bHasPositionUpdated(false),
SL_PlayerObject(NULL),
SL_PlayerPlayInterface(NULL),
SL_PlayerBufferQueue(NULL),
SL_VolumeInterface(NULL),
RealtimeAsyncTask(NULL)
{
Buffer = 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 (SLESBuffer && SLESBuffer->ResourceID == 0)
{
delete SLESBuffer;
}
SLESBuffer = nullptr;
Buffer = nullptr;
}
/**
* 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;
}
FSoundSource::UpdateCommon();
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);
// Set whether to apply reverb
SetReverbApplied(true);
SetFilterFrequency();
// 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;
SLESBuffer = nullptr;
Buffer = nullptr;
}
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;
}
if (WaveInstance && WaveInstance->LoopingMode == LOOP_Never)
{
// if it wasn't that simple, see if we're at the end position
SLmillisecond PositionMs;
SLmillisecond DurationMs;
result = (*SL_PlayerPlayInterface)->GetPosition(SL_PlayerPlayInterface, &PositionMs);
check(SL_RESULT_SUCCESS == result);
result = (*SL_PlayerPlayInterface)->GetDuration(SL_PlayerPlayInterface, &DurationMs);
check(SL_RESULT_SUCCESS == result);
// on some android devices, the value for GetPosition wraps back to 0 when the playback is done, however it's very possible
// for us to try to check for IsSourceFinished when the Position is genuinely "0". Therefore, we'll flip bHasPositionUpdated once
// we've actually started the sound to denote a wrap-back 0 position versus a real 0 position
if ((DurationMs != SL_TIME_UNKNOWN && PositionMs == DurationMs) || (PositionMs == 0 && bHasPositionUpdated))
{
return true;
}
else if (!bHasPositionUpdated && PositionMs > 0)
{
bHasPositionUpdated = 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;
}