Files
UnrealEngineUWP/Engine/Source/Runtime/SignalProcessing/Private/MultithreadedPatching.cpp
Josh Markiewicz d79515867d Copying //UE4/Dev-Online to Dev-Main (//UE4/Dev-Main)
- Up to CL8320930 from DevOnline and 8311605 Merge Down from Main
- skipped some Fortnite content/plugins/code where it tried to reintegrate files that had been moved pending investigation
#rb none

[CL 8321295 by Josh Markiewicz in Main branch]
2019-08-26 18:35:22 -04:00

214 lines
5.0 KiB
C++

// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
#include "DSP/MultithreadedPatching.h"
#include "DSP/BufferVectorOperations.h"
namespace Audio
{
TAtomic<int32> FPatchOutput::PatchIDCounter(0);
FPatchOutput::FPatchOutput(int32 MaxCapacity, float InGain /*= 1.0f*/)
: InternalBuffer(MaxCapacity)
, TargetGain(InGain)
, PreviousGain(InGain)
, PatchID(++PatchIDCounter)
{
}
int32 FPatchOutput::PopAudio(float* OutBuffer, int32 NumSamples, bool bUseLatestAudio)
{
if (bUseLatestAudio && InternalBuffer.Num() > (uint32) NumSamples)
{
InternalBuffer.SetNum(((uint32)NumSamples));
}
int32 PopResult = InternalBuffer.Pop(OutBuffer, NumSamples);
if (FMath::IsNearlyEqual(TargetGain, PreviousGain))
{
MultiplyBufferByConstantInPlace(OutBuffer, PopResult, PreviousGain);
}
else
{
FadeBufferFast(OutBuffer, PopResult, PreviousGain, TargetGain);
PreviousGain = TargetGain;
}
return PopResult;
}
int32 FPatchOutput::MixInAudio(float* OutBuffer, int32 NumSamples, bool bUseLatestAudio)
{
MixingBuffer.SetNumUninitialized(NumSamples, false);
int32 PopResult = 0;
if (bUseLatestAudio && InternalBuffer.Num() > (uint32)NumSamples)
{
InternalBuffer.SetNum(((uint32)NumSamples));
PopResult = InternalBuffer.Peek(MixingBuffer.GetData(), NumSamples);
}
else
{
PopResult = InternalBuffer.Pop(MixingBuffer.GetData(), NumSamples);
}
if (FMath::IsNearlyEqual(TargetGain, PreviousGain))
{
MixInBufferFast(MixingBuffer.GetData(), OutBuffer, PopResult, PreviousGain);
}
else
{
MixInBufferFast(MixingBuffer.GetData(), OutBuffer, PopResult, PreviousGain, TargetGain);
PreviousGain = TargetGain;
}
return PopResult;
}
FPatchInput::FPatchInput(const FPatchOutputPtr& InOutput)
: OutputHandle(InOutput)
{
}
int32 FPatchInput::PushAudio(const float* InBuffer, int32 NumSamples)
{
FPatchOutputPtr OutputPtr = OutputHandle.Pin();
if (!OutputPtr.IsValid())
{
return -1;
}
int32 SamplesPushed = OutputPtr->InternalBuffer.Push(InBuffer, NumSamples);
return SamplesPushed;
}
void FPatchInput::SetGain(float InGain)
{
FPatchOutputPtr OutputPtr = OutputHandle.Pin();
if (!OutputPtr.IsValid())
{
return;
}
OutputPtr->TargetGain = InGain;
}
bool FPatchInput::IsOutputStillActive()
{
return OutputHandle.IsValid();
}
FPatchMixer::FPatchMixer()
{
}
FPatchInput FPatchMixer::AddNewPatch(int32 MaxLatencyInSamples, float InGain)
{
FScopeLock ScopeLock(&PendingNewPatchesCriticalSection);
int32 NewPatchIndex = PendingNewPatches.Emplace(new FPatchOutput(MaxLatencyInSamples, InGain));
return FPatchInput(PendingNewPatches[NewPatchIndex]);
}
void FPatchMixer::RemovePatch(const FPatchInput& PatchInput)
{
FPatchOutputPtr OutputPtr = PatchInput.OutputHandle.Pin();
// If the output is already disconnected, early exit.
if (!OutputPtr.IsValid())
{
return;
}
FScopeLock ScopeLock(&PatchDeletionCriticalSection);
DisconnectedPatches.Add(OutputPtr->PatchID);
}
int32 FPatchMixer::PopAudio(float* OutBuffer, int32 OutNumSamples, bool bUseLatestAudio)
{
CleanUpDisconnectedPatches();
ConnectNewPatches();
FMemory::Memzero(OutBuffer, OutNumSamples * sizeof(float));
int32 MaxPoppedSamples = 0;
for (FPatchOutputPtr& OutputPtr : CurrentPatches)
{
const int32 NumPoppedSamples = OutputPtr->MixInAudio(OutBuffer, OutNumSamples, bUseLatestAudio);
MaxPoppedSamples = FMath::Max(NumPoppedSamples, MaxPoppedSamples);
}
return MaxPoppedSamples;
}
void FPatchMixer::ConnectNewPatches()
{
// If AddNewPatch is called in a separate thread, wait until the next PopAudio call to do this work.
if (PendingNewPatchesCriticalSection.TryLock())
{
// Todo: convert this to move semantics to avoid copying the shared pointer around.
for (FPatchOutputPtr& Patch : PendingNewPatches)
{
CurrentPatches.Add(Patch);
}
PendingNewPatches.Reset();
PendingNewPatchesCriticalSection.Unlock();
}
}
void FPatchMixer::CleanUpDisconnectedPatches()
{
if (PatchDeletionCriticalSection.TryLock())
{
// Iterate through all of the PatchIDs we need to clean up.
for (const int32& PatchID : DisconnectedPatches)
{
bool bPatchRemoved = false;
// First, make sure that the patch isn't in the pending new patchs we haven't added yet:
{
FScopeLock ScopeLock(&PendingNewPatchesCriticalSection);
for (int32 Index = 0; Index < PendingNewPatches.Num(); Index++)
{
checkSlow(CurrentPatches[Index].IsValid());
if (PatchID == PendingNewPatches[Index]->PatchID)
{
PendingNewPatches.RemoveAtSwap(Index);
bPatchRemoved = true;
break;
}
}
}
if (bPatchRemoved)
{
continue;
}
// Next, we check out current patchs.
for (int32 Index = 0; Index < CurrentPatches.Num(); Index++)
{
checkSlow(CurrentPatches[Index].IsValid());
if (PatchID == CurrentPatches[Index]->PatchID)
{
CurrentPatches.RemoveAtSwap(Index);
break;
}
}
}
DisconnectedPatches.Reset();
PatchDeletionCriticalSection.Unlock();
}
}
}