#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(UserData); return API->StreamCallback((SampleType**)Input, static_cast(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, 20); // 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) { 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 FPortAudioAPI::GetDevices() { TArray 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 FPortAudioAPI::GetHosts() { TArray 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(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& 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; }