mirror of
https://github.com/hrydgard/ppsspp.git
synced 2024-11-23 05:19:56 +00:00
Cleanup: Split out the WASAPI code into its own file.
This commit is contained in:
parent
f90439a70e
commit
f8ea364371
@ -1791,6 +1791,10 @@ add_dependencies(${CoreLibName} GitVersion)
|
|||||||
set(WindowsFiles
|
set(WindowsFiles
|
||||||
Windows/DSoundStream.cpp
|
Windows/DSoundStream.cpp
|
||||||
Windows/DSoundStream.h
|
Windows/DSoundStream.h
|
||||||
|
Windows/WindowsAudio.cpp
|
||||||
|
Windows/WindowsAudio.h
|
||||||
|
Windows/WASAPIStream.cpp
|
||||||
|
Windows/WASAPIStream.h
|
||||||
Windows/Debugger/CPURegsInterface.h
|
Windows/Debugger/CPURegsInterface.h
|
||||||
Windows/Debugger/BreakpointWindow.cpp
|
Windows/Debugger/BreakpointWindow.cpp
|
||||||
Windows/Debugger/BreakpointWindow.h
|
Windows/Debugger/BreakpointWindow.h
|
||||||
|
@ -35,7 +35,7 @@
|
|||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
#include "Windows/DSoundStream.h"
|
#include "Windows/WindowsAudio.h"
|
||||||
#include "Windows/MainWindow.h"
|
#include "Windows/MainWindow.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#include "Common/CommonWindows.h"
|
#include "Common/CommonWindows.h"
|
||||||
#include <mmreg.h>
|
#include <MMReg.h>
|
||||||
#include <process.h>
|
#include <process.h>
|
||||||
|
|
||||||
#ifdef __MINGW32__
|
#ifdef __MINGW32__
|
||||||
@ -11,70 +11,14 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "thread/threadutil.h"
|
#include "thread/threadutil.h"
|
||||||
|
#include "Common/Log.h"
|
||||||
#include "Common/OSVersion.h"
|
#include "Common/OSVersion.h"
|
||||||
#include "Core/ConfigValues.h"
|
#include "Core/ConfigValues.h"
|
||||||
#include "Core/Reporting.h"
|
#include "Core/Reporting.h"
|
||||||
#include "Core/Util/AudioFormat.h"
|
#include "Core/Util/AudioFormat.h"
|
||||||
#include "Windows/W32Util/Misc.h"
|
#include "Windows/W32Util/Misc.h"
|
||||||
|
|
||||||
#include "dsoundstream.h"
|
#include "DSoundStream.h"
|
||||||
|
|
||||||
// WASAPI begin
|
|
||||||
#include <Objbase.h>
|
|
||||||
#include <Mmreg.h>
|
|
||||||
#include <MMDeviceAPI.h>
|
|
||||||
#include <AudioClient.h>
|
|
||||||
#include <AudioPolicy.h>
|
|
||||||
|
|
||||||
#pragma comment(lib, "ole32.lib")
|
|
||||||
|
|
||||||
const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator);
|
|
||||||
const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator);
|
|
||||||
const IID IID_IAudioClient = __uuidof(IAudioClient);
|
|
||||||
const IID IID_IAudioRenderClient = __uuidof(IAudioRenderClient);
|
|
||||||
// WASAPI end
|
|
||||||
|
|
||||||
#define BUFSIZE 0x4000
|
|
||||||
#define MAXWAIT 20 //ms
|
|
||||||
|
|
||||||
class DSoundAudioBackend : public WindowsAudioBackend {
|
|
||||||
public:
|
|
||||||
DSoundAudioBackend();
|
|
||||||
~DSoundAudioBackend() override;
|
|
||||||
|
|
||||||
bool Init(HWND window, StreamCallback callback, int sampleRate) override; // If fails, can safely delete the object
|
|
||||||
void Update() override;
|
|
||||||
int GetSampleRate() override { return sampleRate_; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
inline int ModBufferSize(int x) { return (x + bufferSize_) % bufferSize_; }
|
|
||||||
int RunThread();
|
|
||||||
static unsigned int WINAPI soundThread(void *param);
|
|
||||||
bool CreateBuffer();
|
|
||||||
bool WriteDataToBuffer(DWORD offset, // Our own write cursor.
|
|
||||||
char* soundData, // Start of our data.
|
|
||||||
DWORD soundBytes); // Size of block to copy.
|
|
||||||
|
|
||||||
CRITICAL_SECTION soundCriticalSection;
|
|
||||||
HWND window_;
|
|
||||||
HANDLE soundSyncEvent_ = NULL;
|
|
||||||
HANDLE hThread_ = NULL;
|
|
||||||
|
|
||||||
StreamCallback callback_;
|
|
||||||
|
|
||||||
IDirectSound8 *ds_ = NULL;
|
|
||||||
IDirectSoundBuffer *dsBuffer_ = NULL;
|
|
||||||
|
|
||||||
int bufferSize_; // bytes
|
|
||||||
int totalRenderedBytes_;
|
|
||||||
int sampleRate_;
|
|
||||||
|
|
||||||
volatile int threadData_;
|
|
||||||
|
|
||||||
int currentPos_;
|
|
||||||
int lastPos_;
|
|
||||||
short realtimeBuffer_[BUFSIZE * 2];
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: Get rid of this
|
// TODO: Get rid of this
|
||||||
static DSoundAudioBackend *g_dsound;
|
static DSoundAudioBackend *g_dsound;
|
||||||
@ -250,239 +194,3 @@ void DSoundAudioBackend::Update() {
|
|||||||
if (soundSyncEvent_ != NULL)
|
if (soundSyncEvent_ != NULL)
|
||||||
SetEvent(soundSyncEvent_);
|
SetEvent(soundSyncEvent_);
|
||||||
}
|
}
|
||||||
|
|
||||||
class WASAPIAudioBackend : public WindowsAudioBackend {
|
|
||||||
public:
|
|
||||||
WASAPIAudioBackend();
|
|
||||||
~WASAPIAudioBackend() override;
|
|
||||||
|
|
||||||
bool Init(HWND window, StreamCallback callback, int sampleRate) override; // If fails, can safely delete the object
|
|
||||||
void Update() override {}
|
|
||||||
int GetSampleRate() override { return sampleRate_; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
int RunThread();
|
|
||||||
static unsigned int WINAPI soundThread(void *param);
|
|
||||||
|
|
||||||
HANDLE hThread_;
|
|
||||||
|
|
||||||
StreamCallback callback_;
|
|
||||||
int sampleRate_;
|
|
||||||
|
|
||||||
volatile int threadData_;
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: Make these adjustable. This is from the example in MSDN.
|
|
||||||
// 200 times/sec = 5ms, pretty good :) Wonder if all computers can handle it though.
|
|
||||||
#define REFTIMES_PER_SEC (10000000/200)
|
|
||||||
#define REFTIMES_PER_MILLISEC (REFTIMES_PER_SEC / 1000)
|
|
||||||
|
|
||||||
WASAPIAudioBackend::WASAPIAudioBackend() : hThread_(NULL), sampleRate_(0), callback_(nullptr), threadData_(0) {
|
|
||||||
}
|
|
||||||
|
|
||||||
WASAPIAudioBackend::~WASAPIAudioBackend() {
|
|
||||||
if (threadData_ == 0) {
|
|
||||||
threadData_ = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hThread_ != NULL) {
|
|
||||||
WaitForSingleObject(hThread_, 1000);
|
|
||||||
CloseHandle(hThread_);
|
|
||||||
hThread_ = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (threadData_ == 2) {
|
|
||||||
// blah.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int WINAPI WASAPIAudioBackend::soundThread(void *param) {
|
|
||||||
WASAPIAudioBackend *backend = (WASAPIAudioBackend *)param;
|
|
||||||
return backend->RunThread();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool WASAPIAudioBackend::Init(HWND window, StreamCallback callback, int sampleRate) {
|
|
||||||
threadData_ = 0;
|
|
||||||
callback_ = callback;
|
|
||||||
sampleRate_ = sampleRate;
|
|
||||||
hThread_ = (HANDLE)_beginthreadex(0, 0, soundThread, (void *)this, 0, 0);
|
|
||||||
SetThreadPriority(hThread_, THREAD_PRIORITY_ABOVE_NORMAL);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
int WASAPIAudioBackend::RunThread() {
|
|
||||||
// Adapted from http://msdn.microsoft.com/en-us/library/windows/desktop/dd316756(v=vs.85).aspx
|
|
||||||
|
|
||||||
CoInitializeEx(NULL, COINIT_MULTITHREADED);
|
|
||||||
setCurrentThreadName("WASAPI_audio");
|
|
||||||
|
|
||||||
IMMDeviceEnumerator *pDeviceEnumerator;
|
|
||||||
IMMDevice *pDevice;
|
|
||||||
IAudioClient *pAudioInterface;
|
|
||||||
IAudioRenderClient *pAudioRenderClient;
|
|
||||||
WAVEFORMATEXTENSIBLE *pDeviceFormat;
|
|
||||||
DWORD flags = 0;
|
|
||||||
REFERENCE_TIME hnsBufferDuration, hnsActualDuration;
|
|
||||||
UINT32 pNumBufferFrames;
|
|
||||||
UINT32 pNumPaddingFrames, pNumAvFrames;
|
|
||||||
short *shortBuf = nullptr;
|
|
||||||
int numSamples;
|
|
||||||
hnsBufferDuration = REFTIMES_PER_SEC;
|
|
||||||
|
|
||||||
enum {
|
|
||||||
UNKNOWN_FORMAT = 0,
|
|
||||||
IEEE_FLOAT = 1,
|
|
||||||
PCM16 = 2,
|
|
||||||
} format = UNKNOWN_FORMAT;
|
|
||||||
|
|
||||||
HRESULT hresult;
|
|
||||||
hresult = CoCreateInstance(CLSID_MMDeviceEnumerator,
|
|
||||||
NULL, /*Object is not created as the part of the aggregate */
|
|
||||||
CLSCTX_ALL, IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator);
|
|
||||||
if (FAILED(hresult)) goto bail;
|
|
||||||
|
|
||||||
hresult = pDeviceEnumerator->GetDefaultAudioEndpoint(eRender, eMultimedia, &pDevice);
|
|
||||||
if (FAILED(hresult)) {
|
|
||||||
pDeviceEnumerator->Release();
|
|
||||||
goto bail;
|
|
||||||
}
|
|
||||||
|
|
||||||
hresult = pDevice->Activate(IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pAudioInterface);
|
|
||||||
if (FAILED(hresult)) {
|
|
||||||
pDevice->Release();
|
|
||||||
pDeviceEnumerator->Release();
|
|
||||||
goto bail;
|
|
||||||
}
|
|
||||||
|
|
||||||
hresult = pAudioInterface->GetMixFormat((WAVEFORMATEX**)&pDeviceFormat);
|
|
||||||
hresult = pAudioInterface->Initialize(AUDCLNT_SHAREMODE_SHARED, 0, hnsBufferDuration, 0, &pDeviceFormat->Format, NULL);
|
|
||||||
hresult = pAudioInterface->GetService(IID_IAudioRenderClient, (void**)&pAudioRenderClient);
|
|
||||||
if (FAILED(hresult)) {
|
|
||||||
pDevice->Release();
|
|
||||||
pDeviceEnumerator->Release();
|
|
||||||
pAudioInterface->Release();
|
|
||||||
goto bail;
|
|
||||||
}
|
|
||||||
hresult = pAudioInterface->GetBufferSize(&pNumBufferFrames);
|
|
||||||
if (FAILED(hresult)) {
|
|
||||||
pDevice->Release();
|
|
||||||
pDeviceEnumerator->Release();
|
|
||||||
pAudioInterface->Release();
|
|
||||||
goto bail;
|
|
||||||
}
|
|
||||||
|
|
||||||
sampleRate_ = pDeviceFormat->Format.nSamplesPerSec;
|
|
||||||
|
|
||||||
// Don't know if PCM16 ever shows up here, the documentation only talks about float... but let's blindly
|
|
||||||
// try to support it :P
|
|
||||||
|
|
||||||
if (pDeviceFormat->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
|
|
||||||
if (!memcmp(&pDeviceFormat->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, sizeof(pDeviceFormat->SubFormat))) {
|
|
||||||
format = IEEE_FLOAT;
|
|
||||||
// printf("float format\n");
|
|
||||||
} else {
|
|
||||||
ERROR_LOG_REPORT_ONCE(unexpectedformat, SCEAUDIO, "Got unexpected WASAPI 0xFFFE stream format, expected float!");
|
|
||||||
if (pDeviceFormat->Format.wBitsPerSample == 16 && pDeviceFormat->Format.nChannels == 2) {
|
|
||||||
format = PCM16;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ERROR_LOG_REPORT_ONCE(unexpectedformat2, SCEAUDIO, "Got unexpected non-extensible WASAPI stream format, expected extensible float!");
|
|
||||||
if (pDeviceFormat->Format.wBitsPerSample == 16 && pDeviceFormat->Format.nChannels == 2) {
|
|
||||||
format = PCM16;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
BYTE *pData;
|
|
||||||
hresult = pAudioRenderClient->GetBuffer(pNumBufferFrames, &pData);
|
|
||||||
numSamples = pNumBufferFrames * pDeviceFormat->Format.nChannels;
|
|
||||||
if (format == IEEE_FLOAT) {
|
|
||||||
memset(pData, 0, sizeof(float) * numSamples);
|
|
||||||
shortBuf = new short[pNumBufferFrames * pDeviceFormat->Format.nChannels];
|
|
||||||
} else if (format == PCM16) {
|
|
||||||
memset(pData, 0, sizeof(short) * numSamples);
|
|
||||||
}
|
|
||||||
|
|
||||||
hresult = pAudioRenderClient->ReleaseBuffer(pNumBufferFrames, flags);
|
|
||||||
hnsActualDuration = (REFERENCE_TIME)((double)REFTIMES_PER_SEC * pNumBufferFrames / pDeviceFormat->Format.nSamplesPerSec);
|
|
||||||
|
|
||||||
hresult = pAudioInterface->Start();
|
|
||||||
|
|
||||||
while (flags != AUDCLNT_BUFFERFLAGS_SILENT) {
|
|
||||||
Sleep((DWORD)(hnsActualDuration / REFTIMES_PER_MILLISEC / 2));
|
|
||||||
|
|
||||||
hresult = pAudioInterface->GetCurrentPadding(&pNumPaddingFrames);
|
|
||||||
if (FAILED(hresult)) {
|
|
||||||
// What to do?
|
|
||||||
pNumPaddingFrames = 0;
|
|
||||||
}
|
|
||||||
pNumAvFrames = pNumBufferFrames - pNumPaddingFrames;
|
|
||||||
|
|
||||||
hresult = pAudioRenderClient->GetBuffer(pNumAvFrames, &pData);
|
|
||||||
if (FAILED(hresult)) {
|
|
||||||
// What to do?
|
|
||||||
} else if (pNumAvFrames) {
|
|
||||||
switch (format) {
|
|
||||||
case IEEE_FLOAT:
|
|
||||||
callback_(shortBuf, pNumAvFrames, 16, sampleRate_, 2);
|
|
||||||
if (pDeviceFormat->Format.nChannels == 2) {
|
|
||||||
ConvertS16ToF32((float *)pData, shortBuf, pNumAvFrames * pDeviceFormat->Format.nChannels);
|
|
||||||
} else {
|
|
||||||
float *ptr = (float *)pData;
|
|
||||||
int chans = pDeviceFormat->Format.nChannels;
|
|
||||||
memset(ptr, 0, pNumAvFrames * chans * sizeof(float));
|
|
||||||
for (UINT32 i = 0; i < pNumAvFrames; i++) {
|
|
||||||
ptr[i * chans + 0] = (float)shortBuf[i * 2] * (1.0f / 32768.0f);
|
|
||||||
ptr[i * chans + 1] = (float)shortBuf[i * 2 + 1] * (1.0f / 32768.0f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case PCM16:
|
|
||||||
callback_((short *)pData, pNumAvFrames, 16, sampleRate_, 2);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (threadData_ != 0) {
|
|
||||||
flags = AUDCLNT_BUFFERFLAGS_SILENT;
|
|
||||||
}
|
|
||||||
|
|
||||||
hresult = pAudioRenderClient->ReleaseBuffer(pNumAvFrames, flags);
|
|
||||||
if (FAILED(hresult)) {
|
|
||||||
// Not much to do here either...
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for last data in buffer to play before stopping.
|
|
||||||
Sleep((DWORD)(hnsActualDuration / REFTIMES_PER_MILLISEC / 2));
|
|
||||||
|
|
||||||
delete[] shortBuf;
|
|
||||||
hresult = pAudioInterface->Stop();
|
|
||||||
|
|
||||||
CoTaskMemFree(pDeviceFormat);
|
|
||||||
pDeviceEnumerator->Release();
|
|
||||||
pDevice->Release();
|
|
||||||
pAudioInterface->Release();
|
|
||||||
pAudioRenderClient->Release();
|
|
||||||
|
|
||||||
bail:
|
|
||||||
threadData_ = 2;
|
|
||||||
CoUninitialize();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
WindowsAudioBackend *CreateAudioBackend(AudioBackendType type) {
|
|
||||||
if (IsVistaOrHigher()) {
|
|
||||||
switch (type) {
|
|
||||||
case AUDIO_BACKEND_WASAPI:
|
|
||||||
case AUDIO_BACKEND_AUTO:
|
|
||||||
return new WASAPIAudioBackend();
|
|
||||||
case AUDIO_BACKEND_DSOUND:
|
|
||||||
default:
|
|
||||||
return new DSoundAudioBackend();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return new DSoundAudioBackend();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,21 +1,51 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "Common/CommonWindows.h"
|
// This should only be included from WindowsAudio.cpp and DSoundStream.cpp.
|
||||||
#include "Core/Config.h"
|
|
||||||
#include "Core/ConfigValues.h"
|
|
||||||
|
|
||||||
typedef int (*StreamCallback)(short *buffer, int numSamples, int bits, int rate, int channels);
|
#include "WindowsAudio.h"
|
||||||
|
#include <mmreg.h>
|
||||||
|
#include <dsound.h>
|
||||||
|
|
||||||
// Note that the backend may override the passed in sample rate. The actual sample rate
|
class DSoundAudioBackend : public WindowsAudioBackend {
|
||||||
// should be returned by GetSampleRate though.
|
|
||||||
class WindowsAudioBackend {
|
|
||||||
public:
|
public:
|
||||||
WindowsAudioBackend() {}
|
DSoundAudioBackend();
|
||||||
virtual ~WindowsAudioBackend() {}
|
~DSoundAudioBackend() override;
|
||||||
virtual bool Init(HWND window, StreamCallback _callback, int sampleRate) = 0;
|
|
||||||
virtual void Update() {} // Doesn't have to do anything
|
|
||||||
virtual int GetSampleRate() = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Factory
|
bool Init(HWND window, StreamCallback callback, int sampleRate) override; // If fails, can safely delete the object
|
||||||
WindowsAudioBackend *CreateAudioBackend(AudioBackendType type);
|
void Update() override;
|
||||||
|
int GetSampleRate() override { return sampleRate_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
inline int ModBufferSize(int x) { return (x + bufferSize_) % bufferSize_; }
|
||||||
|
int RunThread();
|
||||||
|
static unsigned int WINAPI soundThread(void *param);
|
||||||
|
bool CreateBuffer();
|
||||||
|
bool WriteDataToBuffer(DWORD offset, // Our own write cursor.
|
||||||
|
char* soundData, // Start of our data.
|
||||||
|
DWORD soundBytes); // Size of block to copy.
|
||||||
|
|
||||||
|
CRITICAL_SECTION soundCriticalSection;
|
||||||
|
HWND window_;
|
||||||
|
HANDLE soundSyncEvent_ = nullptr;
|
||||||
|
HANDLE hThread_ = nullptr;
|
||||||
|
|
||||||
|
StreamCallback callback_;
|
||||||
|
|
||||||
|
IDirectSound8 *ds_ = nullptr;
|
||||||
|
IDirectSoundBuffer *dsBuffer_ = nullptr;
|
||||||
|
|
||||||
|
int bufferSize_; // bytes
|
||||||
|
int totalRenderedBytes_;
|
||||||
|
int sampleRate_;
|
||||||
|
|
||||||
|
volatile int threadData_;
|
||||||
|
|
||||||
|
enum {
|
||||||
|
BUFSIZE = 0x4000,
|
||||||
|
MAXWAIT = 20, //ms
|
||||||
|
};
|
||||||
|
|
||||||
|
int currentPos_;
|
||||||
|
int lastPos_;
|
||||||
|
short realtimeBuffer_[BUFSIZE * 2];
|
||||||
|
};
|
||||||
|
@ -398,6 +398,8 @@
|
|||||||
<ClCompile Include="MainWindow.cpp" />
|
<ClCompile Include="MainWindow.cpp" />
|
||||||
<ClCompile Include="DSoundStream.cpp" />
|
<ClCompile Include="DSoundStream.cpp" />
|
||||||
<ClCompile Include="GPU\WindowsGLContext.cpp" />
|
<ClCompile Include="GPU\WindowsGLContext.cpp" />
|
||||||
|
<ClCompile Include="WASAPIStream.cpp" />
|
||||||
|
<ClCompile Include="WindowsAudio.cpp" />
|
||||||
<ClCompile Include="WindowsHost.cpp" />
|
<ClCompile Include="WindowsHost.cpp" />
|
||||||
<ClCompile Include="main.cpp" />
|
<ClCompile Include="main.cpp" />
|
||||||
<ClCompile Include="stdafx.cpp">
|
<ClCompile Include="stdafx.cpp">
|
||||||
@ -499,6 +501,8 @@
|
|||||||
<ClInclude Include="MainWindow.h" />
|
<ClInclude Include="MainWindow.h" />
|
||||||
<ClInclude Include="DSoundStream.h" />
|
<ClInclude Include="DSoundStream.h" />
|
||||||
<ClInclude Include="GPU\WindowsGLContext.h" />
|
<ClInclude Include="GPU\WindowsGLContext.h" />
|
||||||
|
<ClInclude Include="WASAPIStream.h" />
|
||||||
|
<ClInclude Include="WindowsAudio.h" />
|
||||||
<ClInclude Include="WindowsHost.h" />
|
<ClInclude Include="WindowsHost.h" />
|
||||||
<ClInclude Include="main.h" />
|
<ClInclude Include="main.h" />
|
||||||
<ClInclude Include="resource.h" />
|
<ClInclude Include="resource.h" />
|
||||||
|
@ -188,6 +188,12 @@
|
|||||||
<ClCompile Include="..\UWP\UWPHost.cpp">
|
<ClCompile Include="..\UWP\UWPHost.cpp">
|
||||||
<Filter>Other Platforms</Filter>
|
<Filter>Other Platforms</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="WindowsAudio.cpp">
|
||||||
|
<Filter>Windows\System</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="WASAPIStream.cpp">
|
||||||
|
<Filter>Windows\System</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="Debugger\CtrlDisAsmView.h">
|
<ClInclude Include="Debugger\CtrlDisAsmView.h">
|
||||||
@ -347,6 +353,12 @@
|
|||||||
<ClInclude Include="..\UWP\UWPHost.h">
|
<ClInclude Include="..\UWP\UWPHost.h">
|
||||||
<Filter>Other Platforms</Filter>
|
<Filter>Other Platforms</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="WindowsAudio.h">
|
||||||
|
<Filter>Windows\System</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="WASAPIStream.h">
|
||||||
|
<Filter>Windows\System</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="icon1.ico">
|
<None Include="icon1.ico">
|
||||||
|
219
Windows/WASAPIStream.cpp
Normal file
219
Windows/WASAPIStream.cpp
Normal file
@ -0,0 +1,219 @@
|
|||||||
|
#include "WindowsAudio.h"
|
||||||
|
#include "WASAPIStream.h"
|
||||||
|
#include "Common/Log.h"
|
||||||
|
#include "Core/Reporting.h"
|
||||||
|
#include "Core/Util/AudioFormat.h"
|
||||||
|
|
||||||
|
#include "thread/threadutil.h"
|
||||||
|
|
||||||
|
#include <Objbase.h>
|
||||||
|
#include <Mmreg.h>
|
||||||
|
#include <MMDeviceAPI.h>
|
||||||
|
#include <AudioClient.h>
|
||||||
|
#include <AudioPolicy.h>
|
||||||
|
|
||||||
|
#pragma comment(lib, "ole32.lib")
|
||||||
|
|
||||||
|
const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator);
|
||||||
|
const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator);
|
||||||
|
const IID IID_IAudioClient = __uuidof(IAudioClient);
|
||||||
|
const IID IID_IAudioRenderClient = __uuidof(IAudioRenderClient);
|
||||||
|
|
||||||
|
// TODO: Make these adjustable. This is from the example in MSDN.
|
||||||
|
// 200 times/sec = 5ms, pretty good :) Wonder if all computers can handle it though.
|
||||||
|
#define REFTIMES_PER_SEC (10000000/200)
|
||||||
|
#define REFTIMES_PER_MILLISEC (REFTIMES_PER_SEC / 1000)
|
||||||
|
|
||||||
|
WASAPIAudioBackend::WASAPIAudioBackend() : hThread_(nullptr), sampleRate_(0), callback_(nullptr), threadData_(0) {
|
||||||
|
}
|
||||||
|
|
||||||
|
WASAPIAudioBackend::~WASAPIAudioBackend() {
|
||||||
|
if (threadData_ == 0) {
|
||||||
|
threadData_ = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hThread_) {
|
||||||
|
WaitForSingleObject(hThread_, 1000);
|
||||||
|
CloseHandle(hThread_);
|
||||||
|
hThread_ = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (threadData_ == 2) {
|
||||||
|
// blah.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int WINAPI WASAPIAudioBackend::soundThread(void *param) {
|
||||||
|
WASAPIAudioBackend *backend = (WASAPIAudioBackend *)param;
|
||||||
|
return backend->RunThread();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WASAPIAudioBackend::Init(HWND window, StreamCallback callback, int sampleRate) {
|
||||||
|
threadData_ = 0;
|
||||||
|
callback_ = callback;
|
||||||
|
sampleRate_ = sampleRate;
|
||||||
|
hThread_ = (HANDLE)_beginthreadex(0, 0, soundThread, (void *)this, 0, 0);
|
||||||
|
SetThreadPriority(hThread_, THREAD_PRIORITY_ABOVE_NORMAL);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int WASAPIAudioBackend::RunThread() {
|
||||||
|
// Adapted from http://msdn.microsoft.com/en-us/library/windows/desktop/dd316756(v=vs.85).aspx
|
||||||
|
|
||||||
|
CoInitializeEx(NULL, COINIT_MULTITHREADED);
|
||||||
|
setCurrentThreadName("WASAPI_audio");
|
||||||
|
|
||||||
|
IMMDeviceEnumerator *pDeviceEnumerator;
|
||||||
|
IMMDevice *pDevice;
|
||||||
|
IAudioClient *pAudioInterface;
|
||||||
|
IAudioRenderClient *pAudioRenderClient;
|
||||||
|
WAVEFORMATEXTENSIBLE *pDeviceFormat;
|
||||||
|
DWORD flags = 0;
|
||||||
|
REFERENCE_TIME hnsBufferDuration, hnsActualDuration;
|
||||||
|
UINT32 pNumBufferFrames;
|
||||||
|
UINT32 pNumPaddingFrames, pNumAvFrames;
|
||||||
|
hnsBufferDuration = REFTIMES_PER_SEC;
|
||||||
|
|
||||||
|
HRESULT hresult;
|
||||||
|
hresult = CoCreateInstance(CLSID_MMDeviceEnumerator,
|
||||||
|
NULL, /*Object is not created as the part of the aggregate */
|
||||||
|
CLSCTX_ALL, IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator);
|
||||||
|
if (FAILED(hresult)) goto bail;
|
||||||
|
|
||||||
|
hresult = pDeviceEnumerator->GetDefaultAudioEndpoint(eRender, eMultimedia, &pDevice);
|
||||||
|
if (FAILED(hresult)) {
|
||||||
|
pDeviceEnumerator->Release();
|
||||||
|
goto bail;
|
||||||
|
}
|
||||||
|
|
||||||
|
hresult = pDevice->Activate(IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pAudioInterface);
|
||||||
|
if (FAILED(hresult)) {
|
||||||
|
pDevice->Release();
|
||||||
|
pDeviceEnumerator->Release();
|
||||||
|
goto bail;
|
||||||
|
}
|
||||||
|
|
||||||
|
hresult = pAudioInterface->GetMixFormat((WAVEFORMATEX**)&pDeviceFormat);
|
||||||
|
hresult = pAudioInterface->Initialize(AUDCLNT_SHAREMODE_SHARED, 0, hnsBufferDuration, 0, &pDeviceFormat->Format, NULL);
|
||||||
|
hresult = pAudioInterface->GetService(IID_IAudioRenderClient, (void**)&pAudioRenderClient);
|
||||||
|
if (FAILED(hresult)) {
|
||||||
|
pDevice->Release();
|
||||||
|
pDeviceEnumerator->Release();
|
||||||
|
pAudioInterface->Release();
|
||||||
|
goto bail;
|
||||||
|
}
|
||||||
|
hresult = pAudioInterface->GetBufferSize(&pNumBufferFrames);
|
||||||
|
if (FAILED(hresult)) {
|
||||||
|
pDevice->Release();
|
||||||
|
pDeviceEnumerator->Release();
|
||||||
|
pAudioInterface->Release();
|
||||||
|
goto bail;
|
||||||
|
}
|
||||||
|
|
||||||
|
sampleRate_ = pDeviceFormat->Format.nSamplesPerSec;
|
||||||
|
|
||||||
|
enum {
|
||||||
|
UNKNOWN_FORMAT = 0,
|
||||||
|
IEEE_FLOAT = 1,
|
||||||
|
PCM16 = 2,
|
||||||
|
} format = UNKNOWN_FORMAT;
|
||||||
|
|
||||||
|
// Don't know if PCM16 ever shows up here, the documentation only talks about float... but let's blindly
|
||||||
|
// try to support it :P
|
||||||
|
|
||||||
|
if (pDeviceFormat->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
|
||||||
|
if (!memcmp(&pDeviceFormat->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, sizeof(pDeviceFormat->SubFormat))) {
|
||||||
|
format = IEEE_FLOAT;
|
||||||
|
// printf("float format\n");
|
||||||
|
} else {
|
||||||
|
ERROR_LOG_REPORT_ONCE(unexpectedformat, SCEAUDIO, "Got unexpected WASAPI 0xFFFE stream format, expected float!");
|
||||||
|
if (pDeviceFormat->Format.wBitsPerSample == 16 && pDeviceFormat->Format.nChannels == 2) {
|
||||||
|
format = PCM16;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ERROR_LOG_REPORT_ONCE(unexpectedformat2, SCEAUDIO, "Got unexpected non-extensible WASAPI stream format, expected extensible float!");
|
||||||
|
if (pDeviceFormat->Format.wBitsPerSample == 16 && pDeviceFormat->Format.nChannels == 2) {
|
||||||
|
format = PCM16;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
short *shortBuf = nullptr;
|
||||||
|
|
||||||
|
BYTE *pData;
|
||||||
|
hresult = pAudioRenderClient->GetBuffer(pNumBufferFrames, &pData);
|
||||||
|
int numSamples = pNumBufferFrames * pDeviceFormat->Format.nChannels;
|
||||||
|
if (format == IEEE_FLOAT) {
|
||||||
|
memset(pData, 0, sizeof(float) * numSamples);
|
||||||
|
shortBuf = new short[pNumBufferFrames * pDeviceFormat->Format.nChannels];
|
||||||
|
} else if (format == PCM16) {
|
||||||
|
memset(pData, 0, sizeof(short) * numSamples);
|
||||||
|
}
|
||||||
|
|
||||||
|
hresult = pAudioRenderClient->ReleaseBuffer(pNumBufferFrames, flags);
|
||||||
|
hnsActualDuration = (REFERENCE_TIME)((double)REFTIMES_PER_SEC * pNumBufferFrames / pDeviceFormat->Format.nSamplesPerSec);
|
||||||
|
|
||||||
|
hresult = pAudioInterface->Start();
|
||||||
|
|
||||||
|
while (flags != AUDCLNT_BUFFERFLAGS_SILENT) {
|
||||||
|
Sleep((DWORD)(hnsActualDuration / REFTIMES_PER_MILLISEC / 2));
|
||||||
|
|
||||||
|
hresult = pAudioInterface->GetCurrentPadding(&pNumPaddingFrames);
|
||||||
|
if (FAILED(hresult)) {
|
||||||
|
// What to do?
|
||||||
|
pNumPaddingFrames = 0;
|
||||||
|
}
|
||||||
|
pNumAvFrames = pNumBufferFrames - pNumPaddingFrames;
|
||||||
|
|
||||||
|
hresult = pAudioRenderClient->GetBuffer(pNumAvFrames, &pData);
|
||||||
|
if (FAILED(hresult)) {
|
||||||
|
// What to do?
|
||||||
|
} else if (pNumAvFrames) {
|
||||||
|
switch (format) {
|
||||||
|
case IEEE_FLOAT:
|
||||||
|
callback_(shortBuf, pNumAvFrames, 16, sampleRate_, 2);
|
||||||
|
if (pDeviceFormat->Format.nChannels == 2) {
|
||||||
|
ConvertS16ToF32((float *)pData, shortBuf, pNumAvFrames * pDeviceFormat->Format.nChannels);
|
||||||
|
} else {
|
||||||
|
float *ptr = (float *)pData;
|
||||||
|
int chans = pDeviceFormat->Format.nChannels;
|
||||||
|
memset(ptr, 0, pNumAvFrames * chans * sizeof(float));
|
||||||
|
for (UINT32 i = 0; i < pNumAvFrames; i++) {
|
||||||
|
ptr[i * chans + 0] = (float)shortBuf[i * 2] * (1.0f / 32768.0f);
|
||||||
|
ptr[i * chans + 1] = (float)shortBuf[i * 2 + 1] * (1.0f / 32768.0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PCM16:
|
||||||
|
callback_((short *)pData, pNumAvFrames, 16, sampleRate_, 2);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (threadData_ != 0) {
|
||||||
|
flags = AUDCLNT_BUFFERFLAGS_SILENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
hresult = pAudioRenderClient->ReleaseBuffer(pNumAvFrames, flags);
|
||||||
|
if (FAILED(hresult)) {
|
||||||
|
// Not much to do here either...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for last data in buffer to play before stopping.
|
||||||
|
Sleep((DWORD)(hnsActualDuration / REFTIMES_PER_MILLISEC / 2));
|
||||||
|
|
||||||
|
delete[] shortBuf;
|
||||||
|
hresult = pAudioInterface->Stop();
|
||||||
|
|
||||||
|
CoTaskMemFree(pDeviceFormat);
|
||||||
|
pDeviceEnumerator->Release();
|
||||||
|
pDevice->Release();
|
||||||
|
pAudioInterface->Release();
|
||||||
|
pAudioRenderClient->Release();
|
||||||
|
|
||||||
|
bail:
|
||||||
|
threadData_ = 2;
|
||||||
|
CoUninitialize();
|
||||||
|
return 0;
|
||||||
|
}
|
26
Windows/WASAPIStream.h
Normal file
26
Windows/WASAPIStream.h
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "WindowsAudio.h"
|
||||||
|
|
||||||
|
// This should only be included from WindowsAudio.cpp and WASAPIStream.cpp.
|
||||||
|
|
||||||
|
class WASAPIAudioBackend : public WindowsAudioBackend {
|
||||||
|
public:
|
||||||
|
WASAPIAudioBackend();
|
||||||
|
~WASAPIAudioBackend() override;
|
||||||
|
|
||||||
|
bool Init(HWND window, StreamCallback callback, int sampleRate) override; // If fails, can safely delete the object
|
||||||
|
void Update() override {}
|
||||||
|
int GetSampleRate() override { return sampleRate_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
int RunThread();
|
||||||
|
static unsigned int WINAPI soundThread(void *param);
|
||||||
|
|
||||||
|
HANDLE hThread_;
|
||||||
|
|
||||||
|
StreamCallback callback_;
|
||||||
|
int sampleRate_;
|
||||||
|
|
||||||
|
volatile int threadData_;
|
||||||
|
};
|
19
Windows/WindowsAudio.cpp
Normal file
19
Windows/WindowsAudio.cpp
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#include "Common/OSVersion.h"
|
||||||
|
#include "WindowsAudio.h"
|
||||||
|
#include "DSoundStream.h"
|
||||||
|
#include "WASAPIStream.h"
|
||||||
|
|
||||||
|
WindowsAudioBackend *CreateAudioBackend(AudioBackendType type) {
|
||||||
|
if (IsVistaOrHigher()) {
|
||||||
|
switch (type) {
|
||||||
|
case AUDIO_BACKEND_WASAPI:
|
||||||
|
case AUDIO_BACKEND_AUTO:
|
||||||
|
return new WASAPIAudioBackend();
|
||||||
|
case AUDIO_BACKEND_DSOUND:
|
||||||
|
default:
|
||||||
|
return new DSoundAudioBackend();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return new DSoundAudioBackend();
|
||||||
|
}
|
||||||
|
}
|
21
Windows/WindowsAudio.h
Normal file
21
Windows/WindowsAudio.h
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Common/CommonWindows.h"
|
||||||
|
#include "Core/Config.h"
|
||||||
|
#include "Core/ConfigValues.h"
|
||||||
|
|
||||||
|
typedef int(*StreamCallback)(short *buffer, int numSamples, int bits, int rate, int channels);
|
||||||
|
|
||||||
|
// Note that the backend may override the passed in sample rate. The actual sample rate
|
||||||
|
// should be returned by GetSampleRate though.
|
||||||
|
class WindowsAudioBackend {
|
||||||
|
public:
|
||||||
|
WindowsAudioBackend() {}
|
||||||
|
virtual ~WindowsAudioBackend() {}
|
||||||
|
virtual bool Init(HWND window, StreamCallback _callback, int sampleRate) = 0;
|
||||||
|
virtual void Update() {} // Doesn't have to do anything
|
||||||
|
virtual int GetSampleRate() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Factory
|
||||||
|
WindowsAudioBackend *CreateAudioBackend(AudioBackendType type);
|
@ -47,7 +47,7 @@
|
|||||||
#include "Core/System.h"
|
#include "Core/System.h"
|
||||||
#include "Core/Debugger/SymbolMap.h"
|
#include "Core/Debugger/SymbolMap.h"
|
||||||
#include "Windows/EmuThread.h"
|
#include "Windows/EmuThread.h"
|
||||||
#include "Windows/DSoundStream.h"
|
#include "Windows/WindowsAudio.h"
|
||||||
#include "Windows/WindowsHost.h"
|
#include "Windows/WindowsHost.h"
|
||||||
#include "Windows/MainWindow.h"
|
#include "Windows/MainWindow.h"
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@
|
|||||||
#include "Core/ConfigValues.h"
|
#include "Core/ConfigValues.h"
|
||||||
#include "Core/SaveState.h"
|
#include "Core/SaveState.h"
|
||||||
#include "Windows/EmuThread.h"
|
#include "Windows/EmuThread.h"
|
||||||
#include "Windows/DSoundStream.h"
|
#include "Windows/WindowsAudio.h"
|
||||||
#include "ext/disarm.h"
|
#include "ext/disarm.h"
|
||||||
|
|
||||||
#include "Common/LogManager.h"
|
#include "Common/LogManager.h"
|
||||||
|
Loading…
Reference in New Issue
Block a user