2019-12-26 14:45:42 -05:00
// Copyright Epic Games, Inc. All Rights Reserved.
2018-06-27 16:58:34 -04:00
# include "AudioMixerSourceBuffer.h"
2019-03-05 06:13:19 -05:00
# include "AudioMixerSourceDecode.h"
# include "ContentStreaming.h"
2019-08-26 18:35:22 -04:00
# include "AudioDecompress.h"
2020-02-05 19:30:44 -05:00
# include "Misc/ScopeTryLock.h"
2018-06-27 16:58:34 -04:00
namespace Audio
{
bool FRawPCMDataBuffer : : GetNextBuffer ( FMixerSourceVoiceBuffer * OutSourceBufferPtr , const uint32 NumSampleToGet )
{
// TODO: support loop counts
float * OutBufferPtr = OutSourceBufferPtr - > AudioData . GetData ( ) ;
int16 * DataPtr = ( int16 * ) Data ;
if ( LoopCount = = Audio : : LOOP_FOREVER )
{
2021-07-12 13:36:39 -04:00
bool bIsFinishedOrLooped = false ;
2018-06-27 16:58:34 -04:00
for ( uint32 Sample = 0 ; Sample < NumSampleToGet ; + + Sample )
{
OutBufferPtr [ Sample ] = DataPtr [ CurrentSample + + ] / 32768.0f ;
// Loop around if we're looping
if ( CurrentSample > = NumSamples )
{
CurrentSample = 0 ;
2021-07-12 13:36:39 -04:00
bIsFinishedOrLooped = true ;
2018-06-27 16:58:34 -04:00
}
}
2021-07-12 13:36:39 -04:00
return bIsFinishedOrLooped ;
2018-06-27 16:58:34 -04:00
}
else if ( CurrentSample < NumSamples )
{
uint32 Sample = 0 ;
while ( Sample < NumSampleToGet & & CurrentSample < NumSamples )
{
OutBufferPtr [ Sample + + ] = ( float ) DataPtr [ CurrentSample + + ] / 32768.0f ;
}
// Zero out the rest of the buffer
while ( Sample < NumSampleToGet )
{
OutBufferPtr [ Sample + + ] = 0.0f ;
}
}
else
{
for ( uint32 Sample = 0 ; Sample < NumSampleToGet ; + + Sample )
{
OutBufferPtr [ Sample ] = 0.0f ;
}
}
// If the current sample is greater or equal to num samples we hit the end of the buffer
return CurrentSample > = NumSamples ;
}
2021-04-03 18:41:39 -04:00
TSharedPtr < FMixerSourceBuffer , ESPMode : : ThreadSafe > FMixerSourceBuffer : : Create ( FMixerSourceBufferInitArgs & InArgs )
2019-10-03 00:07:56 -04:00
{
LLM_SCOPE ( ELLMTag : : AudioMixer ) ;
2021-04-03 18:41:39 -04:00
TSharedPtr < FMixerSourceBuffer , ESPMode : : ThreadSafe > NewSourceBuffer = MakeShareable ( new FMixerSourceBuffer ( InArgs ) ) ;
2021-01-11 14:18:46 -04:00
2019-10-03 00:07:56 -04:00
return NewSourceBuffer ;
}
2021-04-03 18:41:39 -04:00
FMixerSourceBuffer : : FMixerSourceBuffer ( FMixerSourceBufferInitArgs & InArgs )
2018-06-27 16:58:34 -04:00
: NumBuffersQeueued ( 0 )
, CurrentBuffer ( 0 )
2021-04-03 18:41:39 -04:00
, SoundWave ( InArgs . SoundWave )
2018-06-27 16:58:34 -04:00
, AsyncRealtimeAudioTask ( nullptr )
2019-03-05 06:13:19 -05:00
, DecompressionState ( nullptr )
2021-04-03 18:41:39 -04:00
, LoopingMode ( InArgs . LoopingMode )
, NumChannels ( InArgs . Buffer - > NumChannels )
, BufferType ( InArgs . Buffer - > GetType ( ) )
, NumPrecacheFrames ( InArgs . SoundWave - > NumPrecacheFrames )
2022-02-25 14:37:02 -05:00
, AuioDeviceID ( InArgs . AudioDeviceID )
2018-07-09 04:56:07 -04:00
, bInitialized ( false )
2018-06-27 16:58:34 -04:00
, bBufferFinished ( false )
, bPlayedCachedBuffer ( false )
2021-04-03 18:41:39 -04:00
, bIsSeeking ( InArgs . bIsSeeking )
2018-06-27 16:58:34 -04:00
, bLoopCallback ( false )
2021-04-03 18:41:39 -04:00
, bProcedural ( InArgs . SoundWave - > bProcedural )
, bIsBus ( InArgs . SoundWave - > bIsSourceBus )
, bForceSyncDecode ( InArgs . bForceSyncDecode )
2021-01-21 16:22:06 -04:00
, bHasError ( false )
2018-06-27 16:58:34 -04:00
{
2020-04-23 01:02:01 -04:00
// TODO: remove the need to do this here. 1) remove need for decoders to depend on USoundWave and 2) remove need for procedural sounds to use USoundWaveProcedural
2021-04-03 18:41:39 -04:00
InArgs . SoundWave - > AddPlayingSource ( this ) ;
2019-10-03 00:07:56 -04:00
2020-04-23 01:02:01 -04:00
// Retrieve a sound generator if this is a procedural sound wave
if ( bProcedural )
{
2020-07-23 20:32:26 -04:00
FSoundGeneratorInitParams InitParams ;
2022-02-07 18:00:45 -05:00
InitParams . AudioDeviceID = InArgs . AudioDeviceID ;
2021-04-03 18:41:39 -04:00
InitParams . SampleRate = InArgs . SampleRate ;
2020-07-23 20:32:26 -04:00
InitParams . NumChannels = NumChannels ;
InitParams . NumFramesPerCallback = MONO_PCM_BUFFER_SAMPLES ;
2021-04-03 18:41:39 -04:00
InitParams . InstanceID = InArgs . InstanceID ;
InitParams . bIsPreviewSound = InArgs . bIsPreviewSound ;
SoundGenerator = InArgs . SoundWave - > CreateSoundGenerator ( InitParams ) ;
2021-07-12 13:36:39 -04:00
// In the case of procedural audio generation, the mixer source buffer will never "loop" -- i.e. when it's done, it's done
LoopingMode = LOOP_Never ;
2020-04-23 01:02:01 -04:00
}
2019-10-03 00:07:56 -04:00
const uint32 TotalSamples = MONO_PCM_BUFFER_SAMPLES * NumChannels ;
for ( int32 BufferIndex = 0 ; BufferIndex < Audio : : MAX_BUFFERS_QUEUED ; + + BufferIndex )
{
2020-03-26 16:11:23 -04:00
SourceVoiceBuffers . Add ( MakeShared < FMixerSourceVoiceBuffer , ESPMode : : ThreadSafe > ( ) ) ;
2019-10-03 00:07:56 -04:00
// Prepare the memory to fit the max number of samples
SourceVoiceBuffers [ BufferIndex ] - > AudioData . Reset ( TotalSamples ) ;
SourceVoiceBuffers [ BufferIndex ] - > bRealTimeBuffer = true ;
SourceVoiceBuffers [ BufferIndex ] - > LoopCount = 0 ;
}
2018-06-27 16:58:34 -04:00
}
FMixerSourceBuffer : : ~ FMixerSourceBuffer ( )
{
2020-02-05 19:30:44 -05:00
// GC methods may get called from the game thread during the destructor
// These methods will trylock and early exit if we have this lock
2020-03-26 16:11:23 -04:00
FScopeLock Lock ( & SoundWaveCritSec ) ;
2019-03-05 06:13:19 -05:00
2020-03-26 16:11:23 -04:00
// OnEndGenerate calls EnsureTaskFinishes,
// which will make sure we have completed our async realtime task before deleting the decompression state
2018-06-27 16:58:34 -04:00
OnEndGenerate ( ) ;
2019-03-05 06:13:19 -05:00
// Clean up decompression state after things have been finished using it
2020-03-26 16:11:23 -04:00
DeleteDecoder ( ) ;
2019-03-08 19:01:38 -05:00
2019-03-12 00:35:32 -04:00
if ( SoundWave )
{
2019-11-22 18:02:26 -05:00
SoundWave - > RemovePlayingSource ( this ) ;
2019-03-12 00:35:32 -04:00
}
2018-06-27 16:58:34 -04:00
}
2019-03-05 06:13:19 -05:00
void FMixerSourceBuffer : : SetDecoder ( ICompressedAudioInfo * InCompressedAudioInfo )
{
2019-03-05 19:34:34 -05:00
if ( DecompressionState = = nullptr )
2019-03-05 06:13:19 -05:00
{
2019-03-05 19:34:34 -05:00
DecompressionState = InCompressedAudioInfo ;
if ( BufferType = = EBufferType : : Streaming )
{
IStreamingManager : : Get ( ) . GetAudioStreamingManager ( ) . AddDecoder ( DecompressionState ) ;
}
2019-03-05 06:13:19 -05:00
}
}
2019-03-05 19:34:34 -05:00
void FMixerSourceBuffer : : SetPCMData ( const FRawPCMDataBuffer & InPCMDataBuffer )
{
2019-03-08 13:55:28 -05:00
check ( BufferType = = EBufferType : : PCM | | BufferType = = EBufferType : : PCMPreview ) ;
2019-03-05 19:34:34 -05:00
RawPCMDataBuffer = InPCMDataBuffer ;
}
void FMixerSourceBuffer : : SetCachedRealtimeFirstBuffers ( TArray < uint8 > & & InPrecachedBuffers )
{
CachedRealtimeFirstBuffer = MoveTemp ( InPrecachedBuffers ) ;
}
2018-07-09 04:56:07 -04:00
bool FMixerSourceBuffer : : Init ( )
2018-06-27 16:58:34 -04:00
{
2018-07-09 04:56:07 -04:00
// We have successfully initialized which means our SoundWave has been flagged as bIsActive
// GC can run between PreInit and Init so when cleaning up FMixerSourceBuffer, we don't want to touch SoundWave unless bInitailized is true.
// SoundWave->bIsSoundActive will prevent GC until it is released in audio render thread
bInitialized = true ;
2018-06-27 16:58:34 -04:00
switch ( BufferType )
{
2020-03-26 16:11:23 -04:00
case EBufferType : : PCM :
case EBufferType : : PCMPreview :
SubmitInitialPCMBuffers ( ) ;
break ;
2018-06-27 16:58:34 -04:00
2020-03-26 16:11:23 -04:00
case EBufferType : : PCMRealTime :
case EBufferType : : Streaming :
SubmitInitialRealtimeBuffers ( ) ;
break ;
2018-06-27 16:58:34 -04:00
2020-03-26 16:11:23 -04:00
case EBufferType : : Invalid :
break ;
2018-06-27 16:58:34 -04:00
}
2018-07-09 04:56:07 -04:00
return true ;
2018-06-27 16:58:34 -04:00
}
void FMixerSourceBuffer : : OnBufferEnd ( )
{
2020-03-26 16:11:23 -04:00
FScopeTryLock Lock ( & SoundWaveCritSec ) ;
2021-01-21 16:22:06 -04:00
if ( ! Lock . IsLocked ( ) | | ( NumBuffersQeueued = = 0 & & bBufferFinished ) | | ( bProcedural & & ! SoundWave ) | | ( bHasError ) )
2018-06-27 16:58:34 -04:00
{
return ;
}
ProcessRealTimeSource ( ) ;
}
2020-03-26 16:11:23 -04:00
int32 FMixerSourceBuffer : : GetNumBuffersQueued ( ) const
2018-06-27 16:58:34 -04:00
{
2020-03-26 16:11:23 -04:00
FScopeTryLock Lock ( & SoundWaveCritSec ) ;
if ( Lock . IsLocked ( ) )
{
return NumBuffersQeueued ;
}
return 0 ;
}
TSharedPtr < FMixerSourceVoiceBuffer , ESPMode : : ThreadSafe > FMixerSourceBuffer : : GetNextBuffer ( )
{
FScopeTryLock Lock ( & SoundWaveCritSec ) ;
2020-03-26 17:19:36 -04:00
if ( ! Lock . IsLocked ( ) )
2020-03-26 16:11:23 -04:00
{
return nullptr ;
}
TSharedPtr < FMixerSourceVoiceBuffer , ESPMode : : ThreadSafe > NewBufferPtr ;
2018-06-27 16:58:34 -04:00
BufferQueue . Dequeue ( NewBufferPtr ) ;
- - NumBuffersQeueued ;
return NewBufferPtr ;
}
void FMixerSourceBuffer : : SubmitInitialPCMBuffers ( )
{
CurrentBuffer = 0 ;
RawPCMDataBuffer . NumSamples = RawPCMDataBuffer . DataSize / sizeof ( int16 ) ;
RawPCMDataBuffer . CurrentSample = 0 ;
// Only submit data if we've successfully loaded it
if ( ! RawPCMDataBuffer . Data | | ! RawPCMDataBuffer . DataSize )
{
return ;
}
RawPCMDataBuffer . LoopCount = ( LoopingMode ! = LOOP_Never ) ? Audio : : LOOP_FOREVER : 0 ;
// Submit the first two format-converted chunks to the source voice
2019-03-05 06:13:19 -05:00
const uint32 NumSamplesPerBuffer = MONO_PCM_BUFFER_SAMPLES * NumChannels ;
2018-06-27 16:58:34 -04:00
int16 * RawPCMBufferDataPtr = ( int16 * ) RawPCMDataBuffer . Data ;
2019-01-09 17:41:16 -05:00
// Prepare the buffer for the PCM submission
SourceVoiceBuffers [ 0 ] - > AudioData . Reset ( NumSamplesPerBuffer ) ;
2019-07-19 09:08:34 -04:00
SourceVoiceBuffers [ 0 ] - > AudioData . AddZeroed ( NumSamplesPerBuffer ) ;
2019-01-09 17:41:16 -05:00
2018-06-27 16:58:34 -04:00
RawPCMDataBuffer . GetNextBuffer ( SourceVoiceBuffers [ 0 ] . Get ( ) , NumSamplesPerBuffer ) ;
SubmitBuffer ( SourceVoiceBuffers [ 0 ] ) ;
CurrentBuffer = 1 ;
}
void FMixerSourceBuffer : : SubmitInitialRealtimeBuffers ( )
{
2018-11-14 19:05:13 -05:00
static_assert ( PLATFORM_NUM_AUDIODECOMPRESSION_PRECACHE_BUFFERS < = 2 & & PLATFORM_NUM_AUDIODECOMPRESSION_PRECACHE_BUFFERS > = 0 , " Unsupported number of precache buffers. " ) ;
2018-06-27 16:58:34 -04:00
CurrentBuffer = 0 ;
bPlayedCachedBuffer = false ;
2019-03-05 19:34:34 -05:00
if ( ! bIsSeeking & & CachedRealtimeFirstBuffer . Num ( ) > 0 )
2018-06-27 16:58:34 -04:00
{
bPlayedCachedBuffer = true ;
2019-03-05 06:13:19 -05:00
const uint32 NumSamples = NumPrecacheFrames * NumChannels ;
2019-01-09 17:41:16 -05:00
const uint32 BufferSize = NumSamples * sizeof ( int16 ) ;
2018-06-27 16:58:34 -04:00
2018-11-14 19:05:13 -05:00
// Format convert the first cached buffers
# if (PLATFORM_NUM_AUDIODECOMPRESSION_PRECACHE_BUFFERS == 2)
2018-06-27 16:58:34 -04:00
{
2019-01-09 17:41:16 -05:00
// Prepare the precache buffer memory
for ( int32 i = 0 ; i < 2 ; + + i )
{
SourceVoiceBuffers [ i ] - > AudioData . Reset ( ) ;
2019-07-19 09:08:34 -04:00
SourceVoiceBuffers [ i ] - > AudioData . AddZeroed ( NumSamples ) ;
2019-01-09 17:41:16 -05:00
}
2019-03-05 19:34:34 -05:00
int16 * CachedBufferPtr0 = ( int16 * ) CachedRealtimeFirstBuffer . GetData ( ) ;
int16 * CachedBufferPtr1 = ( int16 * ) ( CachedRealtimeFirstBuffer . GetData ( ) + BufferSize ) ;
2018-11-14 19:05:13 -05:00
float * AudioData0 = SourceVoiceBuffers [ 0 ] - > AudioData . GetData ( ) ;
float * AudioData1 = SourceVoiceBuffers [ 1 ] - > AudioData . GetData ( ) ;
for ( uint32 Sample = 0 ; Sample < NumSamples ; + + Sample )
{
AudioData0 [ Sample ] = CachedBufferPtr0 [ Sample ] / 32768.0f ;
}
for ( uint32 Sample = 0 ; Sample < NumSamples ; + + Sample )
{
AudioData1 [ Sample ] = CachedBufferPtr1 [ Sample ] / 32768.0f ;
}
// Submit the already decoded and cached audio buffers
SubmitBuffer ( SourceVoiceBuffers [ 0 ] ) ;
SubmitBuffer ( SourceVoiceBuffers [ 1 ] ) ;
CurrentBuffer = 2 ;
2018-06-27 16:58:34 -04:00
}
2018-11-14 19:05:13 -05:00
# elif (PLATFORM_NUM_AUDIODECOMPRESSION_PRECACHE_BUFFERS == 1)
{
2019-01-09 17:41:16 -05:00
SourceVoiceBuffers [ 0 ] - > AudioData . Reset ( ) ;
2019-07-19 09:08:34 -04:00
SourceVoiceBuffers [ 0 ] - > AudioData . AddZeroed ( NumSamples ) ;
2019-01-09 17:41:16 -05:00
2019-03-05 19:34:34 -05:00
int16 * CachedBufferPtr0 = ( int16 * ) CachedRealtimeFirstBuffer . GetData ( ) ;
2018-06-27 16:58:34 -04:00
2018-11-14 19:05:13 -05:00
float * AudioData0 = SourceVoiceBuffers [ 0 ] - > AudioData . GetData ( ) ;
for ( uint32 Sample = 0 ; Sample < NumSamples ; + + Sample )
{
AudioData0 [ Sample ] = CachedBufferPtr0 [ Sample ] / 32768.0f ;
}
2018-06-27 16:58:34 -04:00
2018-11-14 19:05:13 -05:00
// Submit the already decoded and cached audio buffers
SubmitBuffer ( SourceVoiceBuffers [ 0 ] ) ;
CurrentBuffer = 1 ;
}
# endif
2018-06-27 16:58:34 -04:00
}
2019-03-05 19:34:34 -05:00
else if ( ! bIsBus )
2018-06-27 16:58:34 -04:00
{
ProcessRealTimeSource ( ) ;
}
2021-07-12 13:36:39 -04:00
}
2018-06-27 16:58:34 -04:00
2019-03-05 06:13:19 -05:00
bool FMixerSourceBuffer : : ReadMoreRealtimeData ( ICompressedAudioInfo * InDecoder , const int32 BufferIndex , EBufferReadMode BufferReadMode )
2018-06-27 16:58:34 -04:00
{
2019-03-05 06:13:19 -05:00
const int32 MaxSamples = MONO_PCM_BUFFER_SAMPLES * NumChannels ;
2021-07-12 13:36:39 -04:00
2019-01-09 17:41:16 -05:00
SourceVoiceBuffers [ BufferIndex ] - > AudioData . Reset ( ) ;
2019-07-19 09:08:34 -04:00
SourceVoiceBuffers [ BufferIndex ] - > AudioData . AddZeroed ( MaxSamples ) ;
2019-01-09 17:41:16 -05:00
2019-03-05 19:34:34 -05:00
if ( bProcedural )
2018-06-27 16:58:34 -04:00
{
2020-03-26 16:11:23 -04:00
FScopeTryLock Lock ( & SoundWaveCritSec ) ;
if ( Lock . IsLocked ( ) & & SoundWave )
{
FProceduralAudioTaskData NewTaskData ;
2020-04-23 01:02:01 -04:00
// Pass the generator instance to the async task
if ( SoundGenerator . IsValid ( ) )
{
NewTaskData . SoundGenerator = SoundGenerator ;
}
else
{
// Otherwise pass the raw sound wave procedural ptr.
check ( SoundWave & & SoundWave - > bProcedural ) ;
NewTaskData . ProceduralSoundWave = SoundWave ;
}
2020-03-26 16:11:23 -04:00
NewTaskData . AudioData = SourceVoiceBuffers [ BufferIndex ] - > AudioData . GetData ( ) ;
NewTaskData . NumSamples = MaxSamples ;
NewTaskData . NumChannels = NumChannels ;
check ( ! AsyncRealtimeAudioTask ) ;
2022-02-25 14:37:02 -05:00
AsyncRealtimeAudioTask = CreateAudioTask ( AuioDeviceID , NewTaskData ) ;
2020-03-26 16:11:23 -04:00
}
2021-07-12 13:36:39 -04:00
return false ;
2018-06-27 16:58:34 -04:00
}
2019-03-05 06:13:19 -05:00
else if ( BufferType ! = EBufferType : : PCMRealTime & & BufferType ! = EBufferType : : Streaming )
2018-06-27 16:58:34 -04:00
{
check ( RawPCMDataBuffer . Data ! = nullptr ) ;
// Read the next raw PCM buffer into the source buffer index. This converts raw PCM to float.
2019-01-09 17:41:16 -05:00
return RawPCMDataBuffer . GetNextBuffer ( SourceVoiceBuffers [ BufferIndex ] . Get ( ) , MaxSamples ) ;
2018-06-27 16:58:34 -04:00
}
2021-01-21 16:22:06 -04:00
// Handle the case that the decoder has an error and can't continue.
if ( InDecoder & & InDecoder - > HasError ( ) )
{
UE_LOG ( LogAudioMixer , Warning , TEXT ( " Decoder Error, stopping source [%s] " ) ,
2021-02-26 02:30:51 -04:00
* InDecoder - > GetStreamingSoundWave ( ) - > GetFName ( ) . ToString ( ) ) ;
2021-01-21 16:22:06 -04:00
bHasError = true ;
bBufferFinished = true ;
return false ;
}
2019-03-05 06:13:19 -05:00
check ( InDecoder ! = nullptr ) ;
2018-06-27 16:58:34 -04:00
FDecodeAudioTaskData NewTaskData ;
NewTaskData . AudioData = SourceVoiceBuffers [ BufferIndex ] - > AudioData . GetData ( ) ;
2019-03-05 06:13:19 -05:00
NewTaskData . DecompressionState = InDecoder ;
NewTaskData . BufferType = BufferType ;
NewTaskData . NumChannels = NumChannels ;
2018-06-27 16:58:34 -04:00
NewTaskData . bLoopingMode = LoopingMode ! = LOOP_Never ;
NewTaskData . bSkipFirstBuffer = ( BufferReadMode = = EBufferReadMode : : AsynchronousSkipFirstFrame ) ;
NewTaskData . NumFramesToDecode = MONO_PCM_BUFFER_SAMPLES ;
2019-03-05 19:34:34 -05:00
NewTaskData . NumPrecacheFrames = NumPrecacheFrames ;
2020-02-03 20:02:12 -05:00
NewTaskData . bForceSyncDecode = bForceSyncDecode ;
2018-06-27 16:58:34 -04:00
2020-03-26 16:11:23 -04:00
FScopeLock Lock ( & DecodeTaskCritSec ) ;
2018-06-27 16:58:34 -04:00
check ( ! AsyncRealtimeAudioTask ) ;
2022-02-25 14:37:02 -05:00
AsyncRealtimeAudioTask = CreateAudioTask ( AuioDeviceID , NewTaskData ) ;
2018-06-27 16:58:34 -04:00
return false ;
}
2021-07-12 13:36:39 -04:00
void FMixerSourceBuffer : : SubmitRealTimeSourceData ( const bool bInIsFinishedOrLooped )
2018-06-27 16:58:34 -04:00
{
// Have we reached the end of the sound
2021-07-12 13:36:39 -04:00
if ( bInIsFinishedOrLooped )
2018-06-27 16:58:34 -04:00
{
switch ( LoopingMode )
{
2021-07-12 13:36:39 -04:00
case LOOP_Never :
// Play out any queued buffers - once there are no buffers left, the state check at the beginning of IsFinished will fire
bBufferFinished = true ;
break ;
2018-06-27 16:58:34 -04:00
2021-07-12 13:36:39 -04:00
case LOOP_WithNotification :
// If we have just looped, and we are looping, send notification
// This will trigger a WaveInstance->NotifyFinished() in the FXAudio2SoundSournce::IsFinished() function on main thread.
bLoopCallback = true ;
break ;
2018-06-27 16:58:34 -04:00
2021-07-12 13:36:39 -04:00
case LOOP_Forever :
// Let the sound loop indefinitely
break ;
2018-06-27 16:58:34 -04:00
}
}
if ( SourceVoiceBuffers [ CurrentBuffer ] - > AudioData . Num ( ) > 0 )
{
SubmitBuffer ( SourceVoiceBuffers [ CurrentBuffer ] ) ;
}
}
void FMixerSourceBuffer : : ProcessRealTimeSource ( )
{
2020-03-26 16:11:23 -04:00
FScopeLock Lock ( & DecodeTaskCritSec ) ;
2018-06-27 16:58:34 -04:00
if ( AsyncRealtimeAudioTask )
{
AsyncRealtimeAudioTask - > EnsureCompletion ( ) ;
2021-07-12 13:36:39 -04:00
bool bIsFinishedOrLooped = false ;
2018-06-27 16:58:34 -04:00
switch ( AsyncRealtimeAudioTask - > GetType ( ) )
{
2021-07-12 13:36:39 -04:00
case EAudioTaskType : : Decode :
{
FDecodeAudioTaskResults TaskResult ;
AsyncRealtimeAudioTask - > GetResult ( TaskResult ) ;
bIsFinishedOrLooped = TaskResult . bIsFinishedOrLooped ;
}
break ;
2018-06-27 16:58:34 -04:00
2021-07-12 13:36:39 -04:00
case EAudioTaskType : : Procedural :
{
FProceduralAudioTaskResults TaskResult ;
AsyncRealtimeAudioTask - > GetResult ( TaskResult ) ;
2018-06-27 16:58:34 -04:00
2021-07-12 13:36:39 -04:00
SourceVoiceBuffers [ CurrentBuffer ] - > AudioData . SetNum ( TaskResult . NumSamplesWritten ) ;
bIsFinishedOrLooped = TaskResult . bIsFinished ;
}
break ;
2018-06-27 16:58:34 -04:00
}
delete AsyncRealtimeAudioTask ;
AsyncRealtimeAudioTask = nullptr ;
2021-07-12 13:36:39 -04:00
SubmitRealTimeSourceData ( bIsFinishedOrLooped ) ;
2018-06-27 16:58:34 -04:00
}
if ( ! AsyncRealtimeAudioTask )
{
// Update the buffer index
if ( + + CurrentBuffer > 2 )
{
CurrentBuffer = 0 ;
}
EBufferReadMode DataReadMode ;
if ( bPlayedCachedBuffer )
{
bPlayedCachedBuffer = false ;
DataReadMode = EBufferReadMode : : AsynchronousSkipFirstFrame ;
}
else
{
DataReadMode = EBufferReadMode : : Asynchronous ;
}
2021-07-12 13:36:39 -04:00
const bool bIsFinishedOrLooped = ReadMoreRealtimeData ( DecompressionState , CurrentBuffer , DataReadMode ) ;
2018-06-27 16:58:34 -04:00
// If this was a synchronous read, then immediately write it
2021-01-21 16:22:06 -04:00
if ( AsyncRealtimeAudioTask = = nullptr & & ! bHasError )
2018-06-27 16:58:34 -04:00
{
2021-07-12 13:36:39 -04:00
SubmitRealTimeSourceData ( bIsFinishedOrLooped ) ;
2018-06-27 16:58:34 -04:00
}
}
}
2020-03-26 16:11:23 -04:00
void FMixerSourceBuffer : : SubmitBuffer ( TSharedPtr < FMixerSourceVoiceBuffer , ESPMode : : ThreadSafe > InSourceVoiceBuffer )
2018-06-27 16:58:34 -04:00
{
NumBuffersQeueued + + ;
BufferQueue . Enqueue ( InSourceVoiceBuffer ) ;
}
2020-03-26 16:11:23 -04:00
void FMixerSourceBuffer : : DeleteDecoder ( )
{
// Clean up decompression state after things have been finished using it
if ( DecompressionState )
{
if ( BufferType = = EBufferType : : Streaming )
{
IStreamingManager : : Get ( ) . GetAudioStreamingManager ( ) . RemoveDecoder ( DecompressionState ) ;
}
delete DecompressionState ;
DecompressionState = nullptr ;
}
}
2020-03-26 17:19:36 -04:00
bool FMixerSourceBuffer : : OnBeginDestroy ( USoundWave * /*Wave*/ )
2019-11-22 18:02:26 -05:00
{
2020-03-26 16:11:23 -04:00
FScopeTryLock Lock ( & SoundWaveCritSec ) ;
2020-02-05 19:30:44 -05:00
// if we don't have the lock, it means we are in ~FMixerSourceBuffer() on another thread
if ( Lock . IsLocked ( ) & & SoundWave )
{
2020-03-26 16:11:23 -04:00
EnsureAsyncTaskFinishes ( ) ;
DeleteDecoder ( ) ;
ClearWave ( ) ;
2020-02-05 19:30:44 -05:00
return true ;
}
2020-03-26 16:11:23 -04:00
2020-02-05 19:30:44 -05:00
return false ;
2019-11-22 18:02:26 -05:00
}
2020-03-26 17:19:36 -04:00
bool FMixerSourceBuffer : : OnIsReadyForFinishDestroy ( USoundWave * /*Wave*/ ) const
2019-11-22 18:02:26 -05:00
{
return false ;
}
2020-03-26 17:19:36 -04:00
void FMixerSourceBuffer : : OnFinishDestroy ( USoundWave * /*Wave*/ )
2019-11-22 18:02:26 -05:00
{
2020-03-26 16:11:23 -04:00
EnsureAsyncTaskFinishes ( ) ;
FScopeTryLock Lock ( & SoundWaveCritSec ) ;
2020-02-05 19:30:44 -05:00
// if we don't have the lock, it means we are in ~FMixerSourceBuffer() on another thread
if ( Lock . IsLocked ( ) & & SoundWave )
{
2020-03-26 16:11:23 -04:00
DeleteDecoder ( ) ;
ClearWave ( ) ;
2020-02-05 19:30:44 -05:00
}
2019-11-22 18:02:26 -05:00
}
2018-06-27 16:58:34 -04:00
bool FMixerSourceBuffer : : IsAsyncTaskInProgress ( ) const
{
2020-03-26 16:11:23 -04:00
FScopeLock Lock ( & DecodeTaskCritSec ) ;
return AsyncRealtimeAudioTask ! = nullptr ;
2018-06-27 16:58:34 -04:00
}
2020-03-26 17:19:36 -04:00
bool FMixerSourceBuffer : : IsAsyncTaskDone ( ) const
2018-06-27 16:58:34 -04:00
{
2020-03-26 16:11:23 -04:00
FScopeLock Lock ( & DecodeTaskCritSec ) ;
2018-06-27 16:58:34 -04:00
if ( AsyncRealtimeAudioTask )
{
return AsyncRealtimeAudioTask - > IsDone ( ) ;
}
2020-03-26 17:19:36 -04:00
return true ;
2018-06-27 16:58:34 -04:00
}
2020-03-26 17:19:36 -04:00
2021-01-11 14:18:46 -04:00
bool FMixerSourceBuffer : : IsGeneratorFinished ( ) const
{
return bProcedural & & SoundGenerator . IsValid ( ) & & SoundGenerator - > IsFinished ( ) ;
}
2020-03-26 17:19:36 -04:00
void FMixerSourceBuffer : : EnsureAsyncTaskFinishes ( )
2018-06-27 16:58:34 -04:00
{
2020-03-26 16:11:23 -04:00
FScopeLock Lock ( & DecodeTaskCritSec ) ;
2020-03-26 17:19:36 -04:00
if ( AsyncRealtimeAudioTask )
{
AsyncRealtimeAudioTask - > CancelTask ( ) ;
2019-02-22 09:59:27 -05:00
delete AsyncRealtimeAudioTask ;
AsyncRealtimeAudioTask = nullptr ;
2018-06-27 16:58:34 -04:00
}
}
void FMixerSourceBuffer : : OnBeginGenerate ( )
{
2020-03-26 16:11:23 -04:00
FScopeTryLock Lock ( & SoundWaveCritSec ) ;
2020-04-23 01:02:01 -04:00
if ( ! Lock . IsLocked ( ) )
2018-06-27 16:58:34 -04:00
{
2020-04-23 01:02:01 -04:00
return ;
}
if ( SoundGenerator . IsValid ( ) )
{
SoundGenerator - > OnBeginGenerate ( ) ;
}
else
{
if ( SoundWave & & bProcedural )
{
check ( SoundWave & & SoundWave - > bProcedural ) ;
SoundWave - > OnBeginGenerate ( ) ;
}
2018-06-27 16:58:34 -04:00
}
}
void FMixerSourceBuffer : : OnEndGenerate ( )
2020-03-26 17:19:36 -04:00
{
2018-06-27 16:58:34 -04:00
// Make sure the async task finishes!
EnsureAsyncTaskFinishes ( ) ;
2020-03-26 16:11:23 -04:00
FScopeTryLock Lock ( & SoundWaveCritSec ) ;
if ( ! Lock . IsLocked ( ) )
{
return ;
}
2020-04-23 01:02:01 -04:00
if ( SoundGenerator . IsValid ( ) )
2018-06-27 16:58:34 -04:00
{
2020-04-23 01:02:01 -04:00
SoundGenerator - > OnEndGenerate ( ) ;
}
else
{
// Only need to call OnEndGenerate and access SoundWave here if we successfully initialized
if ( SoundWave & & bInitialized & & bProcedural )
{
check ( SoundWave & & SoundWave - > bProcedural ) ;
SoundWave - > OnEndGenerate ( ) ;
}
2018-06-27 16:58:34 -04:00
}
}
}