FAudio: Implement and have functional FAudio output
Corrected the current FAudio output code, FAudio api wasn't a direct 1 for 1 code replacement. Adjusted the existing code structure so that FAudioVoiceCallBack struct was being properly called on. Signed-off-by: Zach Bacon <zachbacon@vba-m.com>
This commit is contained in:
parent
0e13cc9346
commit
311b232ee5
|
@ -325,6 +325,7 @@ endif()
|
||||||
# FAudio.
|
# FAudio.
|
||||||
if(ENABLE_FAUDIO)
|
if(ENABLE_FAUDIO)
|
||||||
find_package(FAudio REQUIRED)
|
find_package(FAudio REQUIRED)
|
||||||
|
target_sources(visualboyadvance-m PRIVATE faudio.cpp)
|
||||||
target_link_libraries(visualboyadvance-m FAudio::FAudio)
|
target_link_libraries(visualboyadvance-m FAudio::FAudio)
|
||||||
target_compile_definitions(visualboyadvance-m PRIVATE VBAM_ENABLE_FAUDIO)
|
target_compile_definitions(visualboyadvance-m PRIVATE VBAM_ENABLE_FAUDIO)
|
||||||
endif()
|
endif()
|
||||||
|
|
|
@ -8,42 +8,44 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
// FAudio
|
// FAudio
|
||||||
#include <faudio.h>
|
#include <FAudio.h>
|
||||||
|
|
||||||
// MMDevice API
|
|
||||||
#include <mmdeviceapi.h>
|
#include <mmdeviceapi.h>
|
||||||
|
|
||||||
#include <wx/arrstr.h>
|
#include <wx/arrstr.h>
|
||||||
|
#include <wx/log.h>
|
||||||
|
#include <wx/translation.h>
|
||||||
|
|
||||||
#include "core/base/sound_driver.h"
|
#include "core/base/sound_driver.h"
|
||||||
#include "core/base/system.h"
|
#include "core/base/system.h"
|
||||||
#include "core/gba/gbaGlobals.h"
|
#include "core/gba/gbaGlobals.h"
|
||||||
#include "wx/config/option-proxy.h"
|
#include "wx/config/option-proxy.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
int GetFADevices(FAudio* fa, wxArrayString* names, wxArrayString* ids,
|
int GetFADevices(FAudio* fa, wxArrayString* names, wxArrayString* ids,
|
||||||
const wxString* match)
|
const wxString* match)
|
||||||
{
|
{
|
||||||
HRESULT hr;
|
uint32_t hr;
|
||||||
UINT32 dev_count = 0;
|
uint32_t dev_count = 0;
|
||||||
|
|
||||||
hr = FAudio_GetDeviceCount(fa, &dev_count);
|
hr = FAudio_GetDeviceCount(fa, &dev_count);
|
||||||
|
|
||||||
if (hr != S_OK) {
|
if (hr != 0) {
|
||||||
wxLogError(_("FAudio: Enumerating devices failed!"));
|
wxLogError(_("FAudio: Enumerating devices failed!"));
|
||||||
return true;
|
return -1;
|
||||||
} else {
|
} else {
|
||||||
FAudioDeviceDetails dd;
|
FAudioDeviceDetails dd;
|
||||||
|
|
||||||
for (UINT32 i = 0; i < dev_count; i++) {
|
for (uint32_t i = 0; i < dev_count; i++) {
|
||||||
hr = FAudio_GetDeviceDetails(fa, i, &dd);
|
hr = FAudio_GetDeviceDetails(fa, i, &dd);
|
||||||
|
|
||||||
if (hr != S_OK) {
|
if (hr != 0) {
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
if (ids) {
|
if (ids) {
|
||||||
ids->push_back((wchar_t*) dd.DeviceID); //FAudio is an interesting beast, but not that hard to adapt... once you get used to it, XAudio2 wouldn't need this, but FAudio declares FAudioDeviceDetails as int32_t
|
ids->push_back((wchar_t*) dd.DeviceID);
|
||||||
names->push_back((wchar_t*) dd.DisplayName);
|
names->push_back((wchar_t*) dd.DisplayName);
|
||||||
} else if (*match == dd.DeviceID)
|
} else if (*match == wxString((wchar_t*) dd.DeviceID))
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -52,29 +54,7 @@ int GetFADevices(FAudio* fa, wxArrayString* names, wxArrayString* ids,
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GetFADevices(wxArrayString& names, wxArrayString& ids)
|
int FAGetDev(FAudio* fa)
|
||||||
{
|
|
||||||
HRESULT hr;
|
|
||||||
FAudio* fa = NULL;
|
|
||||||
UINT32 flags = 0;
|
|
||||||
#ifdef _DEBUG
|
|
||||||
flags = FAUDIO_DEBUG_ENGINE;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
hr = FAudioCreate(&fa, flags, FAUDIO_DEFAULT_PROCESSOR); //Apparently this needs 3 parameters, the processor.
|
|
||||||
|
|
||||||
if (hr != S_OK) {
|
|
||||||
wxLogError(_("The FAudio interface failed to initialize!"));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
GetFADevices(fa, &names, &ids, NULL);
|
|
||||||
//fa->Release();
|
|
||||||
FAudio_Release(fa);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int FAGetDev(FAudio* fa)
|
|
||||||
{
|
{
|
||||||
const wxString& audio_device = OPTION(kSoundAudioDevice);
|
const wxString& audio_device = OPTION(kSoundAudioDevice);
|
||||||
if (audio_device.empty())
|
if (audio_device.empty())
|
||||||
|
@ -85,155 +65,50 @@ static int FAGetDev(FAudio* fa)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class FAudio_Output;
|
|
||||||
|
|
||||||
static void faudio_device_changed(FAudio_Output*);
|
|
||||||
|
|
||||||
class FAudio_Device_Notifier : public IMMNotificationClient {
|
|
||||||
volatile LONG registered;
|
|
||||||
IMMDeviceEnumerator* pEnumerator;
|
|
||||||
|
|
||||||
std::wstring last_device;
|
|
||||||
|
|
||||||
CRITICAL_SECTION lock;
|
|
||||||
std::vector<FAudio_Output*> instances;
|
|
||||||
|
|
||||||
public:
|
|
||||||
FAudio_Device_Notifier()
|
|
||||||
: registered(0)
|
|
||||||
{
|
|
||||||
InitializeCriticalSection(&lock);
|
|
||||||
}
|
|
||||||
~FAudio_Device_Notifier()
|
|
||||||
{
|
|
||||||
DeleteCriticalSection(&lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
ULONG STDMETHODCALLTYPE AddRef()
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
ULONG STDMETHODCALLTYPE Release()
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, VOID** ppvInterface)
|
|
||||||
{
|
|
||||||
if (IID_IUnknown == riid) {
|
|
||||||
*ppvInterface = (IUnknown*)this;
|
|
||||||
} else if (__uuidof(IMMNotificationClient) == riid) {
|
|
||||||
*ppvInterface = (IMMNotificationClient*)this;
|
|
||||||
} else {
|
|
||||||
*ppvInterface = NULL;
|
|
||||||
return E_NOINTERFACE;
|
|
||||||
}
|
|
||||||
|
|
||||||
return S_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
HRESULT STDMETHODCALLTYPE OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR pwstrDeviceId)
|
|
||||||
{
|
|
||||||
if (flow == eRender && last_device.compare(pwstrDeviceId) != 0) {
|
|
||||||
last_device = pwstrDeviceId;
|
|
||||||
EnterCriticalSection(&lock);
|
|
||||||
|
|
||||||
for (auto it = instances.begin(); it < instances.end(); ++it) {
|
|
||||||
faudio_device_changed(*it);
|
|
||||||
}
|
|
||||||
|
|
||||||
LeaveCriticalSection(&lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
return S_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
HRESULT STDMETHODCALLTYPE OnDeviceAdded(LPCWSTR pwstrDeviceId) { return S_OK; }
|
|
||||||
HRESULT STDMETHODCALLTYPE OnDeviceRemoved(LPCWSTR pwstrDeviceId) { return S_OK; }
|
|
||||||
HRESULT STDMETHODCALLTYPE OnDeviceStateChanged(LPCWSTR pwstrDeviceId, DWORD dwNewState) { return S_OK; }
|
|
||||||
HRESULT STDMETHODCALLTYPE OnPropertyValueChanged(LPCWSTR pwstrDeviceId, const PROPERTYKEY key) { return S_OK; }
|
|
||||||
|
|
||||||
void do_register(FAudio_Output* p_instance)
|
|
||||||
{
|
|
||||||
if (InterlockedIncrement(®istered) == 1) {
|
|
||||||
pEnumerator = NULL;
|
|
||||||
HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_INPROC_SERVER, __uuidof(IMMDeviceEnumerator), (void**)&pEnumerator);
|
|
||||||
|
|
||||||
if (SUCCEEDED(hr)) {
|
|
||||||
pEnumerator->RegisterEndpointNotificationCallback(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
EnterCriticalSection(&lock);
|
|
||||||
instances.push_back(p_instance);
|
|
||||||
LeaveCriticalSection(&lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
void do_unregister(FAudio_Output* p_instance)
|
|
||||||
{
|
|
||||||
if (InterlockedDecrement(®istered) == 0) {
|
|
||||||
if (pEnumerator) {
|
|
||||||
pEnumerator->UnregisterEndpointNotificationCallback(this);
|
|
||||||
pEnumerator->Release();
|
|
||||||
pEnumerator = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
EnterCriticalSection(&lock);
|
|
||||||
|
|
||||||
for (auto it = instances.begin(); it < instances.end(); ++it) {
|
|
||||||
if (*it == p_instance) {
|
|
||||||
instances.erase(it);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LeaveCriticalSection(&lock);
|
|
||||||
}
|
|
||||||
} g_notifier;
|
|
||||||
|
|
||||||
// Synchronization Event
|
|
||||||
class FAudio_BufferNotify : public FAudioVoiceCallback {
|
class FAudio_BufferNotify : public FAudioVoiceCallback {
|
||||||
public:
|
public:
|
||||||
HANDLE hBufferEndEvent;
|
bool WaitForSignal() {
|
||||||
|
return WaitForSingleObject(buffer_end_event_, 10000) != WAIT_TIMEOUT;
|
||||||
|
}
|
||||||
|
|
||||||
FAudio_BufferNotify()
|
FAudio_BufferNotify()
|
||||||
{
|
{
|
||||||
hBufferEndEvent = NULL;
|
buffer_end_event_ = nullptr;
|
||||||
hBufferEndEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
buffer_end_event_ = CreateEvent(nullptr, FALSE, FALSE, nullptr);
|
||||||
assert(hBufferEndEvent != NULL);
|
assert(buffer_end_event_ != nullptr);
|
||||||
}
|
|
||||||
|
|
||||||
|
OnBufferEnd = &FAudio_BufferNotify::StaticOnBufferEnd;
|
||||||
|
OnVoiceProcessingPassStart = &FAudio_BufferNotify::StaticOnVoiceProcessingPassStart;
|
||||||
|
OnVoiceProcessingPassEnd = &FAudio_BufferNotify::StaticOnVoiceProcessingPassEnd;
|
||||||
|
OnStreamEnd = &FAudio_BufferNotify::StaticOnStreamEnd;
|
||||||
|
OnBufferStart = &FAudio_BufferNotify::StaticOnBufferStart;
|
||||||
|
OnLoopEnd = &FAudio_BufferNotify::StaticOnLoopEnd;
|
||||||
|
OnVoiceError = &FAudio_BufferNotify::StaticOnVoiceError;
|
||||||
|
}
|
||||||
~FAudio_BufferNotify()
|
~FAudio_BufferNotify()
|
||||||
{
|
{
|
||||||
CloseHandle(hBufferEndEvent);
|
CloseHandle(buffer_end_event_);
|
||||||
hBufferEndEvent = NULL;
|
buffer_end_event_ = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
STDMETHOD_(void, OnBufferEnd)
|
private:
|
||||||
(void* pBufferContext)
|
void* buffer_end_event_;
|
||||||
{
|
|
||||||
assert(hBufferEndEvent != NULL);
|
|
||||||
SetEvent(hBufferEndEvent);
|
|
||||||
}
|
|
||||||
|
|
||||||
// dummies:
|
static void StaticOnBufferEnd(FAudioVoiceCallback* callback, void * pBufferContext) {
|
||||||
STDMETHOD_(void, OnVoiceProcessingPassStart)
|
FAudio_BufferNotify* self = static_cast<FAudio_BufferNotify*>(callback);
|
||||||
(UINT32 BytesRequired) {}
|
if (self != nullptr && self->buffer_end_event_ != NULL)
|
||||||
STDMETHOD_(void, OnVoiceProcessingPassEnd)
|
{
|
||||||
() {}
|
SetEvent(self->buffer_end_event_);
|
||||||
STDMETHOD_(void, OnStreamEnd)
|
}
|
||||||
() {}
|
}
|
||||||
STDMETHOD_(void, OnBufferStart)
|
static void StaticOnVoiceProcessingPassStart(FAudioVoiceCallback* callback, uint32_t BytesRequired) {}
|
||||||
(void* pBufferContext) {}
|
static void StaticOnVoiceProcessingPassEnd(FAudioVoiceCallback* callback) {}
|
||||||
STDMETHOD_(void, OnLoopEnd)
|
static void StaticOnStreamEnd(FAudioVoiceCallback* callback) {}
|
||||||
(void* pBufferContext) {}
|
static void StaticOnBufferStart(FAudioVoiceCallback* callback, void * pBufferContext) {}
|
||||||
STDMETHOD_(void, OnVoiceError)
|
static void StaticOnLoopEnd(FAudioVoiceCallback* callback, void * pBufferContext) {}
|
||||||
(void* pBufferContext, HRESULT Error){};
|
static void StaticOnVoiceError(FAudioVoiceCallback* callback, void * pBufferContext, uint32_t Error) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Class Declaration
|
|
||||||
class FAudio_Output
|
class FAudio_Output
|
||||||
: public SoundDriver {
|
: public SoundDriver {
|
||||||
public:
|
public:
|
||||||
|
@ -254,15 +129,15 @@ public:
|
||||||
void device_change();
|
void device_change();
|
||||||
|
|
||||||
// Configuration Changes
|
// Configuration Changes
|
||||||
void setThrottle(unsigned short throttle);
|
void setThrottle(unsigned short throttle_);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool failed;
|
bool failed;
|
||||||
bool initialized;
|
bool initialized;
|
||||||
bool playing;
|
bool playing;
|
||||||
UINT32 freq;
|
uint32_t freq;
|
||||||
UINT32 bufferCount;
|
uint32_t bufferCount;
|
||||||
BYTE* buffers;
|
uint8_t* buffers;
|
||||||
int currentBuffer;
|
int currentBuffer;
|
||||||
int soundBufferLen;
|
int soundBufferLen;
|
||||||
|
|
||||||
|
@ -276,6 +151,34 @@ private:
|
||||||
FAudio_BufferNotify notify; // buffer end notification
|
FAudio_BufferNotify notify; // buffer end notification
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class FAudio_Device_Notifier : public IMMNotificationClient {
|
||||||
|
private:
|
||||||
|
volatile LONG registered;
|
||||||
|
IMMDeviceEnumerator* pEnumerator;
|
||||||
|
|
||||||
|
std::wstring last_device;
|
||||||
|
|
||||||
|
CRITICAL_SECTION lock;
|
||||||
|
std::vector<FAudio_Output*> instances;
|
||||||
|
|
||||||
|
public:
|
||||||
|
FAudio_Device_Notifier();
|
||||||
|
~FAudio_Device_Notifier();
|
||||||
|
ULONG STDMETHODCALLTYPE AddRef();
|
||||||
|
ULONG STDMETHODCALLTYPE Release();
|
||||||
|
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, VOID** ppvInterface);
|
||||||
|
HRESULT STDMETHODCALLTYPE OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR pwstrDeviceId);
|
||||||
|
HRESULT STDMETHODCALLTYPE OnDeviceAdded(LPCWSTR pwstrDeviceId);
|
||||||
|
HRESULT STDMETHODCALLTYPE OnDeviceRemoved(LPCWSTR pwstrDeviceId);
|
||||||
|
HRESULT STDMETHODCALLTYPE OnDeviceStateChanged(LPCWSTR pwstrDeviceId, DWORD dwNewState);
|
||||||
|
HRESULT STDMETHODCALLTYPE OnPropertyValueChanged(LPCWSTR pwstrDeviceId, const PROPERTYKEY key);
|
||||||
|
|
||||||
|
void do_register(FAudio_Output* p_instance);
|
||||||
|
void do_unregister(FAudio_Output* p_instance);
|
||||||
|
};
|
||||||
|
|
||||||
|
FAudio_Device_Notifier f_notifier;
|
||||||
|
|
||||||
// Class Implementation
|
// Class Implementation
|
||||||
FAudio_Output::FAudio_Output()
|
FAudio_Output::FAudio_Output()
|
||||||
{
|
{
|
||||||
|
@ -290,14 +193,14 @@ FAudio_Output::FAudio_Output()
|
||||||
faud = NULL;
|
faud = NULL;
|
||||||
mVoice = NULL;
|
mVoice = NULL;
|
||||||
sVoice = NULL;
|
sVoice = NULL;
|
||||||
ZeroMemory(&buf, sizeof(buf));
|
memset(&buf, NULL, sizeof(buf));
|
||||||
ZeroMemory(&vState, sizeof(vState));
|
memset(&vState, NULL, sizeof(vState));
|
||||||
g_notifier.do_register(this);
|
f_notifier.do_register(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
FAudio_Output::~FAudio_Output()
|
FAudio_Output::~FAudio_Output()
|
||||||
{
|
{
|
||||||
g_notifier.do_unregister(this);
|
f_notifier.do_unregister(this);
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -307,8 +210,7 @@ void FAudio_Output::close()
|
||||||
|
|
||||||
if (sVoice) {
|
if (sVoice) {
|
||||||
if (playing) {
|
if (playing) {
|
||||||
HRESULT hr = FAudioSourceVoice_Stop(sVoice, 0, FAUDIO_COMMIT_NOW);
|
assert(FAudioSourceVoice_Stop(sVoice, 0, FAUDIO_COMMIT_NOW) == 0);
|
||||||
assert(hr == S_OK);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FAudioVoice_DestroyVoice(sVoice);
|
FAudioVoice_DestroyVoice(sVoice);
|
||||||
|
@ -341,15 +243,15 @@ bool FAudio_Output::init(long sampleRate)
|
||||||
if (failed || initialized)
|
if (failed || initialized)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
HRESULT hr;
|
uint32_t hr;
|
||||||
// Initialize FAudio
|
// Initialize FAudio
|
||||||
UINT32 flags = 0;
|
uint32_t flags = 0;
|
||||||
//#ifdef _DEBUG
|
//#ifdef _DEBUG
|
||||||
// flags = FAUDIO_DEBUG_ENGINE;
|
// flags = FAUDIO_DEBUG_ENGINE;
|
||||||
//#endif
|
//#endif
|
||||||
hr = FAudioCreate(&faud, flags, FAUDIO_DEFAULT_PROCESSOR);
|
hr = FAudioCreate(&faud, flags, FAUDIO_DEFAULT_PROCESSOR);
|
||||||
|
|
||||||
if (hr != S_OK) {
|
if (hr != 0) {
|
||||||
wxLogError(_("The FAudio interface failed to initialize!"));
|
wxLogError(_("The FAudio interface failed to initialize!"));
|
||||||
failed = true;
|
failed = true;
|
||||||
return false;
|
return false;
|
||||||
|
@ -361,27 +263,26 @@ bool FAudio_Output::init(long sampleRate)
|
||||||
soundBufferLen = (freq / 60) * 4;
|
soundBufferLen = (freq / 60) * 4;
|
||||||
// create own buffers to store sound data because it must not be
|
// create own buffers to store sound data because it must not be
|
||||||
// manipulated while the voice plays from it
|
// manipulated while the voice plays from it
|
||||||
buffers = (BYTE*)malloc((bufferCount + 1) * soundBufferLen);
|
buffers = (uint8_t*)malloc((bufferCount + 1) * soundBufferLen);
|
||||||
// + 1 because we need one temporary buffer when all others are in use
|
// + 1 because we need one temporary buffer when all others are in use
|
||||||
WAVEFORMATEX wfx;
|
FAudioWaveFormatEx wfx;
|
||||||
ZeroMemory(&wfx, sizeof(wfx));
|
memset(&wfx, NULL, sizeof(wfx));
|
||||||
wfx.wFormatTag = WAVE_FORMAT_PCM;
|
wfx.wFormatTag = FAUDIO_FORMAT_PCM;
|
||||||
wfx.nChannels = 2;
|
wfx.nChannels = 2;
|
||||||
wfx.nSamplesPerSec = freq;
|
wfx.nSamplesPerSec = freq;
|
||||||
wfx.wBitsPerSample = 16;
|
wfx.wBitsPerSample = 16;
|
||||||
wfx.nBlockAlign = wfx.nChannels * (wfx.wBitsPerSample / 8);
|
wfx.nBlockAlign = wfx.nChannels * (wfx.wBitsPerSample / 8);
|
||||||
wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
|
wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
|
||||||
// create sound receiver
|
// create sound receiver
|
||||||
hr = FAudio_CreateMasteringVoice(
|
hr = FAudio_CreateMasteringVoice(faud,
|
||||||
faud,
|
&mVoice,
|
||||||
&mVoice,
|
FAUDIO_DEFAULT_CHANNELS,
|
||||||
FAUDIO_DEFAULT_CHANNELS,
|
FAUDIO_DEFAULT_SAMPLERATE,
|
||||||
FAUDIO_DEFAULT_SAMPLERATE,
|
0,
|
||||||
0,
|
FAGetDev(faud),
|
||||||
FAGetDev(faud),
|
NULL);
|
||||||
NULL);
|
|
||||||
|
|
||||||
if (hr != S_OK) {
|
if (hr != 0) {
|
||||||
wxLogError(_("FAudio: Creating mastering voice failed!"));
|
wxLogError(_("FAudio: Creating mastering voice failed!"));
|
||||||
failed = true;
|
failed = true;
|
||||||
return false;
|
return false;
|
||||||
|
@ -389,10 +290,9 @@ bool FAudio_Output::init(long sampleRate)
|
||||||
|
|
||||||
// create sound emitter
|
// create sound emitter
|
||||||
//This should be FAudio_CreateSourceVoice()
|
//This should be FAudio_CreateSourceVoice()
|
||||||
//hr = faud->CreateSourceVoice(&sVoice, &wfx, 0, 4.0f, ¬ify);
|
hr = FAudio_CreateSourceVoice(faud, &sVoice, &wfx, 0, 4.0f, ¬ify, NULL, NULL);
|
||||||
hr = FAudio_CreateSourceVoice(faud, &sVoice, (const FAudioWaveFormatEx*)&wfx, 0, 4.0f, ¬ify, NULL, NULL);
|
|
||||||
|
|
||||||
if (hr != S_OK) {
|
if (hr != 0) {
|
||||||
wxLogError(_("FAudio: Creating source voice failed!"));
|
wxLogError(_("FAudio: Creating source voice failed!"));
|
||||||
failed = true;
|
failed = true;
|
||||||
return false;
|
return false;
|
||||||
|
@ -400,10 +300,9 @@ bool FAudio_Output::init(long sampleRate)
|
||||||
|
|
||||||
if (OPTION(kSoundUpmix)) {
|
if (OPTION(kSoundUpmix)) {
|
||||||
// set up stereo upmixing
|
// set up stereo upmixing
|
||||||
FAudioDeviceDetails dd;
|
FAudioDeviceDetails dd {};
|
||||||
ZeroMemory(&dd, sizeof(dd));
|
//memset(&dd, NULL, sizeof(dd));
|
||||||
hr = FAudio_GetDeviceDetails(faud, 0, &dd);
|
assert(FAudio_GetDeviceDetails(faud, 0, &dd) == 0);
|
||||||
assert(hr == S_OK);
|
|
||||||
float* matrix = NULL;
|
float* matrix = NULL;
|
||||||
matrix = (float*)malloc(sizeof(float) * 2 * dd.OutputFormat.Format.nChannels);
|
matrix = (float*)malloc(sizeof(float) * 2 * dd.OutputFormat.Format.nChannels);
|
||||||
|
|
||||||
|
@ -499,8 +398,8 @@ bool FAudio_Output::init(long sampleRate)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (matrixAvailable) {
|
if (matrixAvailable) {
|
||||||
hr = FAudioVoice_SetOutputMatrix(sVoice, NULL, 2, dd.OutputFormat.Format.nChannels, matrix, FAUDIO_DEFAULT_CHANNELS); //What I have here for the OperationSet maybe wrong...
|
hr = FAudioVoice_SetOutputMatrix(sVoice, NULL, 2, dd.OutputFormat.Format.nChannels, matrix, FAUDIO_DEFAULT_CHANNELS);
|
||||||
assert(hr == S_OK);
|
assert(hr == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
free(matrix);
|
free(matrix);
|
||||||
|
@ -508,7 +407,7 @@ bool FAudio_Output::init(long sampleRate)
|
||||||
}
|
}
|
||||||
|
|
||||||
hr = FAudioSourceVoice_Start(sVoice, 0, FAUDIO_COMMIT_NOW);
|
hr = FAudioSourceVoice_Start(sVoice, 0, FAUDIO_COMMIT_NOW);
|
||||||
assert(hr == S_OK);
|
assert(hr == 0);
|
||||||
playing = true;
|
playing = true;
|
||||||
currentBuffer = 0;
|
currentBuffer = 0;
|
||||||
device_changed = false;
|
device_changed = false;
|
||||||
|
@ -518,7 +417,7 @@ bool FAudio_Output::init(long sampleRate)
|
||||||
|
|
||||||
void FAudio_Output::write(uint16_t* finalWave, int length)
|
void FAudio_Output::write(uint16_t* finalWave, int length)
|
||||||
{
|
{
|
||||||
UINT32 flags = 0;
|
uint32_t flags = 0;
|
||||||
if (!initialized || failed)
|
if (!initialized || failed)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -548,7 +447,7 @@ void FAudio_Output::write(uint16_t* finalWave, int length)
|
||||||
// the maximum number of buffers is currently queued
|
// the maximum number of buffers is currently queued
|
||||||
if (!coreOptions.speedup && coreOptions.throttle && !gba_joybus_active) {
|
if (!coreOptions.speedup && coreOptions.throttle && !gba_joybus_active) {
|
||||||
// wait for one buffer to finish playing
|
// wait for one buffer to finish playing
|
||||||
if (WaitForSingleObject(notify.hBufferEndEvent, 10000) == WAIT_TIMEOUT) {
|
if (notify.WaitForSignal()) {
|
||||||
device_changed = true;
|
device_changed = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -559,13 +458,13 @@ void FAudio_Output::write(uint16_t* finalWave, int length)
|
||||||
}
|
}
|
||||||
|
|
||||||
// copy & protect the audio data in own memory area while playing it
|
// copy & protect the audio data in own memory area while playing it
|
||||||
CopyMemory(&buffers[currentBuffer * soundBufferLen], finalWave, soundBufferLen);
|
memcpy(&buffers[currentBuffer * soundBufferLen], finalWave, soundBufferLen);
|
||||||
buf.AudioBytes = soundBufferLen;
|
buf.AudioBytes = soundBufferLen;
|
||||||
buf.pAudioData = &buffers[currentBuffer * soundBufferLen];
|
buf.pAudioData = &buffers[currentBuffer * soundBufferLen];
|
||||||
currentBuffer++;
|
currentBuffer++;
|
||||||
currentBuffer %= (bufferCount + 1); // + 1 because we need one temporary buffer
|
currentBuffer %= (bufferCount + 1); // + 1 because we need one temporary buffer
|
||||||
HRESULT hr = FAudioSourceVoice_SubmitSourceBuffer(sVoice, &buf, NULL); // send buffer to queue.
|
uint32_t hr = FAudioSourceVoice_SubmitSourceBuffer(sVoice, &buf, NULL);
|
||||||
assert(hr == S_OK);
|
assert(hr == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FAudio_Output::pause()
|
void FAudio_Output::pause()
|
||||||
|
@ -574,8 +473,8 @@ void FAudio_Output::pause()
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (playing) {
|
if (playing) {
|
||||||
HRESULT hr = FAudioSourceVoice_Stop(sVoice, 0, FAUDIO_COMMIT_NOW);
|
uint32_t hr = FAudioSourceVoice_Stop(sVoice, 0, FAUDIO_COMMIT_NOW);
|
||||||
assert(hr == S_OK);
|
assert(hr == 0);
|
||||||
playing = false;
|
playing = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -586,8 +485,8 @@ void FAudio_Output::resume()
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!playing) {
|
if (!playing) {
|
||||||
HRESULT hr = FAudioSourceVoice_Start(sVoice, 0, FAUDIO_COMMIT_NOW);
|
uint32_t hr = FAudioSourceVoice_Start(sVoice, 0, FAUDIO_COMMIT_NOW);
|
||||||
assert(hr == S_OK);
|
assert(hr == 0);
|
||||||
playing = true;
|
playing = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -598,8 +497,8 @@ void FAudio_Output::reset()
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (playing) {
|
if (playing) {
|
||||||
HRESULT hr = FAudioSourceVoice_Stop(sVoice, 0, FAUDIO_COMMIT_NOW);
|
uint32_t hr = FAudioSourceVoice_Stop(sVoice, 0, FAUDIO_COMMIT_NOW);
|
||||||
assert(hr == S_OK);
|
assert(hr == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
FAudioSourceVoice_FlushSourceBuffers(sVoice);
|
FAudioSourceVoice_FlushSourceBuffers(sVoice);
|
||||||
|
@ -615,15 +514,127 @@ void FAudio_Output::setThrottle(unsigned short throttle_)
|
||||||
if (throttle_ == 0)
|
if (throttle_ == 0)
|
||||||
throttle_ = 100;
|
throttle_ = 100;
|
||||||
|
|
||||||
HRESULT hr = FAudioSourceVoice_SetFrequencyRatio(sVoice, (float)throttle_ / 100.0f, FAUDIO_MAX_FILTER_FREQUENCY);
|
uint32_t hr = FAudioSourceVoice_SetFrequencyRatio(sVoice, (float)throttle_ / 100.0f, FAUDIO_COMMIT_NOW);
|
||||||
assert(hr == S_OK);
|
assert(hr == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void faudio_device_changed(FAudio_Output* instance)
|
FAudio_Device_Notifier::FAudio_Device_Notifier()
|
||||||
|
: registered(0)
|
||||||
{
|
{
|
||||||
instance->device_change();
|
InitializeCriticalSection(&lock);
|
||||||
|
}
|
||||||
|
FAudio_Device_Notifier::~FAudio_Device_Notifier()
|
||||||
|
{
|
||||||
|
DeleteCriticalSection(&lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ULONG STDMETHODCALLTYPE FAudio_Device_Notifier::AddRef()
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ULONG STDMETHODCALLTYPE FAudio_Device_Notifier::Release()
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE FAudio_Device_Notifier::QueryInterface(REFIID riid, VOID** ppvInterface)
|
||||||
|
{
|
||||||
|
if (IID_IUnknown == riid) {
|
||||||
|
*ppvInterface = (IUnknown*)this;
|
||||||
|
} else if (__uuidof(IMMNotificationClient) == riid) {
|
||||||
|
*ppvInterface = (IMMNotificationClient*)this;
|
||||||
|
} else {
|
||||||
|
*ppvInterface = NULL;
|
||||||
|
return E_NOINTERFACE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE FAudio_Device_Notifier::OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR pwstrDeviceId)
|
||||||
|
{
|
||||||
|
if (flow == eRender && last_device.compare(pwstrDeviceId) != 0) {
|
||||||
|
last_device = pwstrDeviceId;
|
||||||
|
EnterCriticalSection(&lock);
|
||||||
|
|
||||||
|
for (auto it = instances.begin(); it < instances.end(); ++it) {
|
||||||
|
(*it)->device_change();
|
||||||
|
}
|
||||||
|
|
||||||
|
LeaveCriticalSection(&lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE FAudio_Device_Notifier::OnDeviceAdded(LPCWSTR pwstrDeviceId) { return S_OK; }
|
||||||
|
HRESULT STDMETHODCALLTYPE FAudio_Device_Notifier::OnDeviceRemoved(LPCWSTR pwstrDeviceId) { return S_OK; }
|
||||||
|
HRESULT STDMETHODCALLTYPE FAudio_Device_Notifier::OnDeviceStateChanged(LPCWSTR pwstrDeviceId, DWORD dwNewState) { return S_OK; }
|
||||||
|
HRESULT STDMETHODCALLTYPE FAudio_Device_Notifier::OnPropertyValueChanged(LPCWSTR pwstrDeviceId, const PROPERTYKEY key) { return S_OK; }
|
||||||
|
|
||||||
|
void FAudio_Device_Notifier::do_register(FAudio_Output* p_instance)
|
||||||
|
{
|
||||||
|
if (InterlockedIncrement(®istered) == 1) {
|
||||||
|
pEnumerator = NULL;
|
||||||
|
HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_INPROC_SERVER, __uuidof(IMMDeviceEnumerator), (void**)&pEnumerator);
|
||||||
|
|
||||||
|
if (SUCCEEDED(hr)) {
|
||||||
|
pEnumerator->RegisterEndpointNotificationCallback(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EnterCriticalSection(&lock);
|
||||||
|
instances.push_back(p_instance);
|
||||||
|
LeaveCriticalSection(&lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FAudio_Device_Notifier::do_unregister(FAudio_Output* p_instance)
|
||||||
|
{
|
||||||
|
if (InterlockedDecrement(®istered) == 0) {
|
||||||
|
if (pEnumerator) {
|
||||||
|
pEnumerator->UnregisterEndpointNotificationCallback(this);
|
||||||
|
pEnumerator->Release();
|
||||||
|
pEnumerator = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EnterCriticalSection(&lock);
|
||||||
|
|
||||||
|
for (auto it = instances.begin(); it < instances.end(); ++it) {
|
||||||
|
if (*it == p_instance) {
|
||||||
|
instances.erase(it);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LeaveCriticalSection(&lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
bool GetFADevices(wxArrayString& names, wxArrayString& ids)
|
||||||
|
{
|
||||||
|
uint32_t hr;
|
||||||
|
FAudio* fa = NULL;
|
||||||
|
uint32_t flags = 0;
|
||||||
|
#ifdef _DEBUG
|
||||||
|
flags = FAUDIO_DEBUG_ENGINE;
|
||||||
|
#endif
|
||||||
|
hr = FAudioCreate(&fa, flags, FAUDIO_DEFAULT_PROCESSOR);
|
||||||
|
|
||||||
|
if (hr != 0) {
|
||||||
|
wxLogError(_("The FAudio interface failed to initialize!"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
GetFADevices(fa, &names, &ids, NULL);
|
||||||
|
FAudio_Release(fa);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Class Declaration
|
||||||
SoundDriver* newFAudio_Output()
|
SoundDriver* newFAudio_Output()
|
||||||
{
|
{
|
||||||
return new FAudio_Output();
|
return new FAudio_Output();
|
||||||
|
|
Loading…
Reference in New Issue