2019-12-26 14:45:42 -05:00
// Copyright Epic Games, Inc. All Rights Reserved.
2019-08-26 18:35:22 -04:00
# pragma once
# include "CoreMinimal.h"
# include "DSP/BufferVectorOperations.h"
# include "DSP/Dsp.h"
2022-06-29 14:58:52 -04:00
class FEvent ;
2019-08-26 18:35:22 -04:00
namespace Audio
{
2022-06-29 14:58:52 -04:00
2019-08-26 18:35:22 -04:00
/**
2019-11-01 15:17:07 -04:00
* This class can be thought of as an output for a single constructed instance of FPatchInput .
* Each FPatchOutput can only be connected to one FPatchInput . To route multiple outputs , see FPatchSplitter .
* To route multiple inputs , see FPatchMixer .
*
* Example usage :
*
* FPatchOutputStrongPtr NewOutput ( new FPatchOutput ( 4096 ) ) ;
* FPatchInput NewInput ( NewOutput ) ;
*
* // On one thread, push audio to the output:
* NewInput . PushAudio ( AudioBufferPtr , AudioBufferNumSamples ) ;
*
* // and on a seperate thread, retrieve the audio:
* NewOutput - > PopAudio ( OutAudioBufferPtr , AudioBufferNumSamples ) ;
*
2019-08-26 18:35:22 -04:00
*/
2023-06-17 18:13:06 -04:00
struct FPatchOutput
2019-08-26 18:35:22 -04:00
{
public :
2023-06-17 18:13:06 -04:00
SIGNALPROCESSING_API FPatchOutput ( int32 InMaxCapacity , float InGain = 1.0f ) ;
2019-08-26 18:35:22 -04:00
2021-10-12 21:21:22 -04:00
/** The default constructor will result in an uninitialized, disconnected patch point. */
2023-06-17 18:13:06 -04:00
SIGNALPROCESSING_API FPatchOutput ( ) ;
2019-11-01 15:17:07 -04:00
2023-06-17 18:13:06 -04:00
SIGNALPROCESSING_API virtual ~ FPatchOutput ( ) ;
2022-06-29 14:58:52 -04:00
2019-11-01 15:17:07 -04:00
/** Copies the minimum of NumSamples or however many samples are available into OutBuffer. Returns the number of samples copied, or -1 if this output's corresponding input has been destroyed. */
2023-06-17 18:13:06 -04:00
SIGNALPROCESSING_API int32 PopAudio ( float * OutBuffer , int32 NumSamples , bool bUseLatestAudio ) ;
2019-08-26 18:35:22 -04:00
/** Sums the minimum of NumSamples or however many samples are available into OutBuffer. Returns the number of samples summed into OutBuffer. */
2023-06-17 18:13:06 -04:00
SIGNALPROCESSING_API int32 MixInAudio ( float * OutBuffer , int32 NumSamples , bool bUseLatestAudio ) ;
2019-08-26 18:35:22 -04:00
2019-11-10 17:18:36 -05:00
/** Returns the current number of samples buffered on this output. */
2023-06-17 18:13:06 -04:00
SIGNALPROCESSING_API int32 GetNumSamplesAvailable ( ) const ;
2019-11-10 17:18:36 -05:00
2022-06-29 14:58:52 -04:00
/** Pauses the current thread until there are the given number of samples available to pop. Will return true if it succeeded, false if it timed out. */
2023-06-17 18:13:06 -04:00
SIGNALPROCESSING_API bool WaitUntilNumSamplesAvailable ( int32 NumSamples , uint32 TimeOutMilliseconds = MAX_uint32 ) ;
2023-02-27 13:29:22 -05:00
2019-11-01 15:17:07 -04:00
/** Returns true if the input for this patch has been destroyed. */
2023-06-17 18:13:06 -04:00
SIGNALPROCESSING_API bool IsInputStale ( ) const ;
2019-11-01 15:17:07 -04:00
2019-08-26 18:35:22 -04:00
friend class FPatchInput ;
friend class FPatchMixer ;
2019-11-01 15:17:07 -04:00
friend class FPatchSplitter ;
2019-08-26 18:35:22 -04:00
private :
2022-06-29 14:58:52 -04:00
2023-06-17 18:13:06 -04:00
SIGNALPROCESSING_API int32 PushAudioToInternalBuffer ( const float * InBuffer , int32 NumSamples ) ;
2022-06-29 14:58:52 -04:00
2019-08-26 18:35:22 -04:00
// Internal buffer.
TCircularAudioBuffer < float > InternalBuffer ;
// For MixInAudio, audio is popped off of InternalBuffer onto here, and then mixed into OutBuffer in MixInAudio.
2021-05-04 16:05:46 -04:00
FAlignedFloatBuffer MixingBuffer ;
2019-08-26 18:35:22 -04:00
// This is applied in PopAudio or MixInAudio.
TAtomic < float > TargetGain ;
float PreviousGain ;
// This is used to breadcrumb the FPatchOutput when we want to delete it.
int32 PatchID ;
2019-11-01 15:17:07 -04:00
// Counter that is incremented/decremented to allow FPatchInput to be copied around safely.
2023-02-27 13:29:22 -05:00
std : : atomic < int32 > NumAliveInputs ;
2019-11-01 15:17:07 -04:00
2022-06-29 14:58:52 -04:00
// Event to pause the current thread until a given number of samples has been filled
2023-02-27 13:29:22 -05:00
std : : atomic < FEvent * > SamplesPushedEvent ;
2023-06-17 18:13:06 -04:00
static SIGNALPROCESSING_API TAtomic < int32 > PatchIDCounter ;
2019-08-26 18:35:22 -04:00
} ;
/** Patch outputs are owned by the FPatchMixer, and are pinned by the FPatchInput. */
2019-11-01 15:17:07 -04:00
typedef TSharedPtr < FPatchOutput , ESPMode : : ThreadSafe > FPatchOutputStrongPtr ;
typedef TWeakPtr < FPatchOutput , ESPMode : : ThreadSafe > FPatchOutputWeakPtr ;
2019-08-26 18:35:22 -04:00
/**
* Handle to a patch . Should only be used by a single thread .
*/
2023-06-17 18:13:06 -04:00
class FPatchInput
2019-08-26 18:35:22 -04:00
{
public :
2019-11-01 15:17:07 -04:00
/** PatchInputs can only be created from explicit outputs. */
2023-06-17 18:13:06 -04:00
SIGNALPROCESSING_API FPatchInput ( const FPatchOutputStrongPtr & InOutput ) ;
SIGNALPROCESSING_API FPatchInput ( const FPatchInput & Other ) ;
SIGNALPROCESSING_API FPatchInput & operator = ( const FPatchInput & Other ) ;
SIGNALPROCESSING_API FPatchInput & operator = ( FPatchInput & & Other ) ;
SIGNALPROCESSING_API FPatchInput ( FPatchInput & & Other ) ;
2019-11-01 15:17:07 -04:00
/** Default constructed FPatchInput instances will always return -1 for PushAudio and true for IsOutputStillActive. */
2021-10-12 21:21:22 -04:00
FPatchInput ( ) = default ;
2019-11-01 15:17:07 -04:00
2023-06-17 18:13:06 -04:00
SIGNALPROCESSING_API ~ FPatchInput ( ) ;
2019-08-26 18:35:22 -04:00
2023-02-27 13:29:22 -05:00
/** Pushes audio from InBuffer to the corresponding FPatchOutput.
* Pushes zeros if InBuffer is nullptr .
2019-08-26 18:35:22 -04:00
* Returns how many samples were able to be pushed , or - 1 if the output was disconnected .
*/
2023-06-17 18:13:06 -04:00
SIGNALPROCESSING_API int32 PushAudio ( const float * InBuffer , int32 NumSamples ) ;
2019-08-26 18:35:22 -04:00
2023-06-17 18:13:06 -04:00
SIGNALPROCESSING_API void SetGain ( float InGain ) ;
2019-08-26 18:35:22 -04:00
/** Returns false if this output was removed, either because someone called FPatchMixer::RemoveTap with this FPatchInput, or the FPatchMixer was destroyed. */
2023-06-17 18:13:06 -04:00
SIGNALPROCESSING_API bool IsOutputStillActive ( ) const ;
2019-08-26 18:35:22 -04:00
2023-06-07 12:51:55 -04:00
/** Returns false if this output was not initialized properly. */
2023-06-17 18:13:06 -04:00
SIGNALPROCESSING_API bool IsValid ( ) const ;
2023-06-07 12:51:55 -04:00
2023-06-17 18:13:06 -04:00
SIGNALPROCESSING_API void Reset ( ) ;
2023-06-07 12:51:55 -04:00
2019-08-26 18:35:22 -04:00
friend class FPatchMixer ;
2019-11-01 15:17:07 -04:00
friend class FPatchSplitter ;
2019-08-26 18:35:22 -04:00
private :
2021-01-09 18:31:09 -04:00
/** Strong pointer to our destination buffer. */
2019-11-01 15:17:07 -04:00
FPatchOutputStrongPtr OutputHandle ;
2021-10-12 21:21:22 -04:00
/** Counter of the number of push calls. */
int32 PushCallsCounter = 0 ;
2019-08-26 18:35:22 -04:00
} ;
/**
* This class is used for retrieving and mixing down audio from multiple threads .
* Important to note that this is MPSC : while multiple threads can enqueue audio on an instance of FPatchMixer using instances of FPatchInput ,
* only one thread can call PopAudio safely .
*/
2023-06-17 18:13:06 -04:00
class FPatchMixer
2019-08-26 18:35:22 -04:00
{
public :
/** Adds a new input to the tap collector. Calling this is thread safe, but individual instances of FPatchInput are only safe to be used from one thread. */
2023-06-17 18:13:06 -04:00
SIGNALPROCESSING_API FPatchInput AddNewInput ( int32 MaxLatencyInSamples , float InGain ) ;
2019-08-26 18:35:22 -04:00
2021-01-09 18:31:09 -04:00
/** Adds an existing patch input to the patch mixer. */
2023-06-17 18:13:06 -04:00
SIGNALPROCESSING_API void AddNewInput ( const FPatchInput & InPatchInput ) ;
2021-01-09 18:31:09 -04:00
2019-08-26 18:35:22 -04:00
/** Removes a tap from the tap collector. Calling this is thread safe, though FPatchOutput will likely not be deleted until the next call of PopAudio. */
2023-06-17 18:13:06 -04:00
SIGNALPROCESSING_API void RemovePatch ( const FPatchInput & InPatchInput ) ;
2019-08-26 18:35:22 -04:00
/** Mixes all inputs into a single buffer. This should only be called from a single thread. Returns the number of non-silent samples popped to OutBuffer. */
2023-06-17 18:13:06 -04:00
SIGNALPROCESSING_API int32 PopAudio ( float * OutBuffer , int32 OutNumSamples , bool bUseLatestAudio ) ;
2019-08-26 18:35:22 -04:00
2019-11-01 15:17:07 -04:00
/** This returns the number of inputs currently connected to this patch mixer. Thread safe, but blocks for PopAudio. */
2023-06-17 18:13:06 -04:00
SIGNALPROCESSING_API int32 Num ( ) ;
2019-11-01 15:17:07 -04:00
/** This function call gets the maximum number of samples that's safe to pop, based on the thread with the least amount of samples buffered. Thread safe, but blocks for PopAudio. */
2023-06-17 18:13:06 -04:00
SIGNALPROCESSING_API int32 MaxNumberOfSamplesThatCanBePopped ( ) ;
2019-11-01 15:17:07 -04:00
2023-02-27 13:29:22 -05:00
/** Pauses the current thread until there are the given number of samples available to pop. Will return true if it succeeded, false if it timed out. */
2023-06-17 18:13:06 -04:00
SIGNALPROCESSING_API bool WaitUntilNumSamplesAvailable ( int32 NumSamples , uint32 TimeOutMilliseconds = MAX_uint32 ) ;
2023-02-27 13:29:22 -05:00
2020-02-09 18:57:53 -05:00
/** Disconnect everything currently connected to this mixer. */
2023-06-17 18:13:06 -04:00
SIGNALPROCESSING_API void DisconnectAllInputs ( ) ;
2020-02-09 18:57:53 -05:00
2019-08-26 18:35:22 -04:00
private :
/** Called within PopAudio. Flushes the PendingNewPatches array into CurrentPatches. During this function, AddNewPatch is blocked. */
void ConnectNewPatches ( ) ;
2020-03-06 15:07:39 -05:00
/** Called within PopAudio and MaxNumberOfSamplesThatCanBePopped. Removes PendingTapsToDelete from CurrentPatches and ConnectNewPatches.
2021-10-12 21:21:22 -04:00
* During this function , RemoveTap and AddNewPatch are blocked . Callers of this function must have CurrentPatchesCriticalSection locked . */
2019-08-26 18:35:22 -04:00
void CleanUpDisconnectedPatches ( ) ;
/** New taps are added here in AddNewPatch, and then are moved to CurrentPatches in ConnectNewPatches. */
2019-11-01 15:17:07 -04:00
TArray < FPatchOutputStrongPtr > PendingNewInputs ;
2019-08-26 18:35:22 -04:00
/** Contended by AddNewPatch, ConnectNewPatches and CleanUpDisconnectedTaps. */
2019-11-01 15:17:07 -04:00
FCriticalSection PendingNewInputsCriticalSection ;
2019-08-26 18:35:22 -04:00
/** Patch IDs of individual audio taps that will be removed on the next call of CleanUpDisconnectedPatches. */
2019-11-01 15:17:07 -04:00
TArray < int32 > DisconnectedInputs ;
2019-08-26 18:35:22 -04:00
/** Contended by RemoveTap, AddNewPatch, and ConnectNewPatches. */
2019-11-01 15:17:07 -04:00
FCriticalSection InputDeletionCriticalSection ;
2019-08-26 18:35:22 -04:00
/** Only accessed within PopAudio. Indirect array of taps that are mixed in during PopAudio. */
2019-11-01 15:17:07 -04:00
TArray < FPatchOutputStrongPtr > CurrentInputs ;
FCriticalSection CurrentPatchesCriticalSection ;
} ;
/**
* This class is used to post audio from one source to multiple threads .
* This class is SPMC : multiple threads can call FPatchOutputStrongPtr - > PopAudio safely ,
* but only one thread can call PushAudio .
*/
2023-06-17 18:13:06 -04:00
class FPatchSplitter
2019-11-01 15:17:07 -04:00
{
public :
/**
* Adds a new output . Calling this is thread safe , but individual instances of FPatchOutput are only safe to be used from one thread .
* the returned FPatchOutputPtr can be safely destroyed at any point .
*/
2023-06-17 18:13:06 -04:00
SIGNALPROCESSING_API FPatchOutputStrongPtr AddNewPatch ( int32 MaxLatencyInSamples , float InGain ) ;
2019-11-01 15:17:07 -04:00
2021-01-09 18:31:09 -04:00
/** Adds a new a patch from an existing patch output. */
2023-06-17 18:13:06 -04:00
SIGNALPROCESSING_API void AddNewPatch ( FPatchOutputStrongPtr & & InPatchOutputStrongPtr ) ;
SIGNALPROCESSING_API void AddNewPatch ( const FPatchOutputStrongPtr & InPatchOutputStrongPtr ) ;
2021-01-09 18:31:09 -04:00
2019-11-01 15:17:07 -04:00
/** This call pushes audio to all outputs connected to this splitter. Only should be called from one thread. */
2023-06-17 18:13:06 -04:00
SIGNALPROCESSING_API int32 PushAudio ( const float * InBuffer , int32 InNumSamples ) ;
2019-11-01 15:17:07 -04:00
/** This returns the number of outputs currently connected to this patch splitter. Thread safe, but blocks for PushAudio. */
2023-06-17 18:13:06 -04:00
SIGNALPROCESSING_API int32 Num ( ) ;
2019-11-01 15:17:07 -04:00
/** This function call gets the maximum number of samples that's safe to push. Thread safe, but blocks for PushAudio. */
2023-06-17 18:13:06 -04:00
SIGNALPROCESSING_API int32 MaxNumberOfSamplesThatCanBePushed ( ) ;
2019-11-01 15:17:07 -04:00
private :
void AddPendingPatches ( ) ;
TArray < FPatchInput > PendingOutputs ;
FCriticalSection PendingOutputsCriticalSection ;
TArray < FPatchInput > ConnectedOutputs ;
FCriticalSection ConnectedOutputsCriticalSection ;
} ;
/**
* This class is used to mix multiple inputs from disparate threads to a single mixdown and deliver that mixdown to multiple outputs .
2021-01-09 18:31:09 -04:00
* This class is MPMC , but only one thread can and should call ProcessAudio ( ) .
2019-11-01 15:17:07 -04:00
*/
2023-06-17 18:13:06 -04:00
class FPatchMixerSplitter
2019-11-01 15:17:07 -04:00
{
public :
/**
* Adds a new output . Calling this is thread safe , but individual instances of FPatchOutput are only safe to be used from one thread .
* the returned FPatchOutputPtr can be safely destroyed at any point .
*/
2023-06-17 18:13:06 -04:00
SIGNALPROCESSING_API FPatchOutputStrongPtr AddNewOutput ( int32 MaxLatencyInSamples , float InGain ) ;
2019-11-01 15:17:07 -04:00
2021-01-09 18:31:09 -04:00
/** Adds a new a patch from an existing patch output. */
2023-06-17 18:13:06 -04:00
SIGNALPROCESSING_API void AddNewOutput ( const FPatchOutputStrongPtr & InPatchOutputStrongPtr ) ;
2021-01-09 18:31:09 -04:00
2019-11-01 15:17:07 -04:00
/** Adds a new input to the tap collector. Calling this is thread safe, but individual instances of FPatchInput are only safe to be used from one thread. */
2023-06-17 18:13:06 -04:00
SIGNALPROCESSING_API FPatchInput AddNewInput ( int32 MaxLatencyInSamples , float InGain ) ;
2019-11-01 15:17:07 -04:00
2021-01-09 18:31:09 -04:00
/** Adds a new a patch input from an existing patch input object. */
2023-06-17 18:13:06 -04:00
SIGNALPROCESSING_API void AddNewInput ( FPatchInput & InInput ) ;
2021-01-09 18:31:09 -04:00
2019-11-01 15:17:07 -04:00
/** Removes a tap from the tap collector. Calling this is thread safe, though FPatchOutput will likely not be deleted until the next call of PopAudio. */
2023-06-17 18:13:06 -04:00
SIGNALPROCESSING_API void RemovePatch ( const FPatchInput & InInput ) ;
2019-11-01 15:17:07 -04:00
/** Mixes audio from all inputs and pushes it to all outputs. Should be called regularly. */
2023-06-17 18:13:06 -04:00
SIGNALPROCESSING_API void ProcessAudio ( ) ;
2019-11-01 15:17:07 -04:00
private :
FPatchMixer Mixer ;
FPatchSplitter Splitter ;
/** This buffer is used to pop audio from our Mixer and push to our splitter. */
2021-05-04 16:05:46 -04:00
FAlignedFloatBuffer IntermediateBuffer ;
2019-08-26 18:35:22 -04:00
} ;
}