156 lines
4.7 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "PortAudioAPI.h"
#include "RunnableThread.h"
#include "Thread/RenderThread.h"
DEFINE_LOG_CATEGORY(PortAudioAPILog);
int PortAudioCallback(const void* Input, void* Output, unsigned long FrameCount, const PaStreamCallbackTimeInfo* TimeInfo, PaStreamCallbackFlags StatusFlags, void* UserData)
{
FPortAudioAPI* API = static_cast<FPortAudioAPI*>(UserData);
return API->StreamCallback((SampleType**)Input, static_cast<SampleType**>(Output), FrameCount, TimeInfo, StatusFlags);
}
FPortAudioAPI::FPortAudioAPI() : InputParameters(), OutputParameters()
{
StreamHandle = nullptr;
InputParameters.sampleFormat = SAMPLE_TYPE_FORMAT | paNonInterleaved;
OutputParameters.sampleFormat = SAMPLE_TYPE_FORMAT | paNonInterleaved;
BlockSize = 512;
SampleRate = 48000.0f;
RenderThread = new FAudioRenderThread();
RenderThreadHandle = nullptr;
OutputParameters.channelCount = 2;
}
void FPortAudioAPI::Init()
{
Pa_Initialize();
OpenStream(0, Pa_GetDefaultOutputDevice());
// OpenStream(0, 1);
LogAllDevices();
UE_LOG(PortAudioAPILog, Log, TEXT("PortAudioAPI Initialized"));
}
void FPortAudioAPI::BeginRelease()
{
Pa_Terminate();
if (RenderThreadHandle)
RenderThreadHandle->Kill();
UE_LOG(PortAudioAPILog, Log, TEXT("PortAudioAPI Released"));
}
void FPortAudioAPI::Release()
{
}
void FPortAudioAPI::OpenStream(int32 InputDevice, int32 OutputDevice)
{
const PaStreamParameters* Input = nullptr;
if (InputDevice > 0)
{
InputParameters.device = InputDevice;
InputParameters.channelCount = 2;
}
OutputParameters.device = OutputDevice;
OutputParameters.channelCount = 2;
RenderThread->SampleRate = SampleRate;
PaError PaError = Pa_OpenStream(&StreamHandle, Input, &OutputParameters, SampleRate, BlockSize, paClipOff, PortAudioCallback, this);
if (PaError != paNoError)
{
UE_LOG(PortAudioAPILog, Error, TEXT("Failed to open stream: %s"), ANSI_TO_TCHAR(Pa_GetErrorText(PaError)));
return;
}
UE_LOG(PortAudioAPILog, Log, TEXT("%s opend"), ANSI_TO_TCHAR(Pa_GetDeviceInfo(OutputDevice)->name));
RenderThread->SetBlockSize(BlockSize);
RenderThreadHandle = FRunnableThread::Create(RenderThread, TEXT("AudioRenderThread"), 0, TPri_TimeCritical);
// while (RenderThread->Buffer.Num() < RenderThread->Size - BlockSize * 2)
// {
// FPlatformProcess::Sleep(0.01f);
// }
PaError = Pa_StartStream(StreamHandle);
if (PaError != paNoError)
{
UE_LOG(PortAudioAPILog, Error, TEXT("Failed to start stream: %s"), ANSI_TO_TCHAR(Pa_GetErrorText(PaError)));
}
}
TArray<FAudioDeviceInfo> FPortAudioAPI::GetDevices()
{
TArray<FAudioDeviceInfo> Out;
const PaDeviceIndex Count = Pa_GetDeviceCount();
for (int i = 0; i < Count; ++i)
{
const PaDeviceInfo* DeviceInfo = Pa_GetDeviceInfo(i);
FAudioDeviceInfo& Info = Out.Add_GetRef(FAudioDeviceInfo());
Info.Name = DeviceInfo->name;
Info.DeviceIndex = i;
Info.MaxInputChannels = DeviceInfo->maxInputChannels;
Info.MaxOutputChannels = DeviceInfo->maxOutputChannels;
Info.DefaultSampleRate = DeviceInfo->defaultSampleRate;
}
return Out;
}
TArray<FAudioHostInfo> FPortAudioAPI::GetHosts()
{
TArray<FAudioHostInfo> Out;
const PaHostApiIndex Count = Pa_GetHostApiCount();
for (int i = 0; i < Count; ++i)
{
const PaHostApiInfo* ApiInfo = Pa_GetHostApiInfo(i);
FAudioHostInfo& Info = Out.Add_GetRef(FAudioHostInfo());
Info.Name = ApiInfo->name;
Info.ID = static_cast<EHostApiID>(ApiInfo->type);
Info.DeviceCount = ApiInfo->deviceCount;
Info.DefaultInputDevice = ApiInfo->defaultInputDevice;
Info.DefaultOutputDevice = ApiInfo->defaultOutputDevice;
}
return Out;
}
void FPortAudioAPI::LogAllDevices()
{
const PaDeviceIndex Count = Pa_GetDeviceCount();
for (int i = 0; i < Count; ++i)
{
const PaDeviceInfo* DeviceInfo = Pa_GetDeviceInfo(i);
UE_LOG(PortAudioAPILog, Log, TEXT("Device %d: %s"), i, ANSI_TO_TCHAR(DeviceInfo->name));
}
}
int32 FPortAudioAPI::GetOutputChannelCount() const
{
return OutputParameters.channelCount;
}
bool FPortAudioAPI::IsInAudioThread()
{
const FRunnableThread* ThreadHandle = Get().RenderThreadHandle;
return ThreadHandle ? ThreadHandle->GetThreadID() == FPlatformTLS::GetCurrentThreadId() : true; // 如果没有RenderThreadHandle说明没有初始化那么就认为是在AudioThread中
}
int FPortAudioAPI::StreamCallback(SampleType** Input, SampleType** Output, unsigned long FrameCount, const PaStreamCallbackTimeInfo* TimeInfo, PaStreamCallbackFlags StatusFlags)
{
Audio::TCircularAudioBuffer<SampleType>& Buffer = RenderThread->Buffer;
if (Buffer.Num() < FrameCount * OutputParameters.channelCount)
return paContinue;
for (int i = 0; i < OutputParameters.channelCount; ++i)
{
Buffer.Pop(Output[i], FrameCount);
}
return paContinue;
}
float FPortAudioAPI::GetBufferLoad()
{
return RenderThread->Buffer.Num() / (float)RenderThread->Size;
}