Files
UnrealEngine/Engine/Source/Runtime/Windows/AudioMixerWasapi/Private/WasapiAggregateRenderStream.cpp
2025-05-18 13:04:45 +08:00

134 lines
3.8 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "WasapiAggregateRenderStream.h"
#include "AudioMixerWasapiLog.h"
#include "DSP/FloatArrayMath.h"
#include "WasapiAudioUtils.h"
namespace Audio
{
bool FWasapiAggregateRenderStream::InitializeHardware(const FWasapiRenderStreamParams& InParams)
{
if (FAudioMixerWasapiRenderStream::InitializeHardware(InParams))
{
DirectOutBuffers.Reset();
DirectOutBuffers.SetNum(InParams.HardwareDeviceInfo.NumChannels);
for (TCircularAudioBuffer<float>& Buffer : DirectOutBuffers)
{
int32 NumOutputBuffers = FMath::Max(InParams.NumBuffers, 2);
int32 BufferCapacity = InParams.NumFrames * NumOutputBuffers;
Buffer.SetCapacity(BufferCapacity);
}
InterleaveBuffers.Reset();
InterleaveBuffers.SetNum(InParams.HardwareDeviceInfo.NumChannels);
const uint32 MinBufferSize = GetMinimumBufferSize(InParams.SampleRate);
WriteNumFrames = FMath::Max(MinBufferSize, InParams.NumFrames);
for (FAlignedFloatBuffer& Buffer : InterleaveBuffers)
{
Buffer.SetNumZeroed(WriteNumFrames);
}
return true;
}
return false;
}
bool FWasapiAggregateRenderStream::StartAudioStream()
{
FAudioMixerWasapiRenderStream::StartAudioStream();
return true;
}
void FWasapiAggregateRenderStream::SubmitDirectOutBuffer(const int32 InChannelIndex, const FAlignedFloatBuffer& InBuffer)
{
if (InChannelIndex >= 0 && InChannelIndex < RenderStreamParams.HardwareDeviceInfo.NumChannels)
{
TCircularAudioBuffer<float>& DirectOutBuffer = DirectOutBuffers[InChannelIndex];
if (InBuffer.Num() == RenderStreamParams.NumFrames)
{
DirectOutBuffer.Push(InBuffer.GetData(), InBuffer.Num());
}
}
}
void FWasapiAggregateRenderStream::DeviceRenderCallback()
{
SCOPED_NAMED_EVENT(FWasapiAggregateRenderStream_DeviceRenderCallback, FColor::Blue);
if (bIsInitialized)
{
uint32 NumFramesPadding = 0;
AudioClient->GetCurrentPadding(&NumFramesPadding);
// NumFramesPerDeviceBuffer is the buffer size WASAPI allocated. It is guaranteed to
// be at least the amount requested. For example, if we request a 1024 frame buffer, WASAPI
// might allocate a 1056 frame buffer. The padding is subtracted from the allocated amount
// to determine how much space is available currently in the buffer.
const int32 NumFramesAvailable = NumFramesPerDeviceBuffer - NumFramesPadding;
if (NumFramesAvailable >= WriteNumFrames)
{
uint8* RenderBufferPtr = nullptr;
if (SUCCEEDED(RenderClient->GetBuffer(WriteNumFrames, &RenderBufferPtr)))
{
InterleaveOutput((float*)RenderBufferPtr, WriteNumFrames);
HRESULT Result = RenderClient->ReleaseBuffer(WriteNumFrames, 0 /* flags */);
if (FAILED(Result))
{
++CallbackBufferErrors;
}
}
else
{
++CallbackBufferErrors;
}
}
}
}
void FWasapiAggregateRenderStream::InterleaveOutput(float* OutRenderBufferPtr, const uint32 InNumFrames)
{
const int32 NumChannels = InterleaveBuffers.Num();
const int32 NumFrames = InterleaveBuffers[0].Num();
if (NumFrames == InNumFrames)
{
for (int32 Index = 0; Index < DirectOutBuffers.Num(); ++Index)
{
// Clear out the interleave buffer
InterleaveBuffers[Index].Reset();
InterleaveBuffers[Index].SetNumZeroed(NumFrames);
if (DirectOutBuffers[Index].Num() >= InNumFrames)
{
const int32 NumFramesPopped = DirectOutBuffers[Index].Pop(InterleaveBuffers[Index].GetData(), InNumFrames);
}
}
TArray<const float*> BufferPtrArray;
BufferPtrArray.Reset(NumChannels);
for (const FAlignedFloatBuffer& Buffer : InterleaveBuffers)
{
const float* BufferPtr = Buffer.GetData();
BufferPtrArray.Add(BufferPtr);
}
const float** InterleaveBufferPtr = BufferPtrArray.GetData();
ArrayInterleave(InterleaveBufferPtr, OutRenderBufferPtr, NumFrames, NumChannels);
}
}
}