FAudio fix ups

Slowly attempt to make FAudio implementation not use imme code
Investigating a cross platform query hardware method
Still a work in progress

Signed-off-by: Zach Bacon <zachbacon@vba-m.com>
This commit is contained in:
Zach Bacon 2024-01-31 11:23:00 -05:00
parent 41552e372a
commit 56056f1e9d
1 changed files with 110 additions and 38 deletions

View File

@ -9,7 +9,7 @@
// FAudio // FAudio
#include <FAudio.h> #include <FAudio.h>
#include <pthread.h>
#include <string> #include <string>
#include <vector> #include <vector>
@ -20,20 +20,24 @@
int GetFADevices(FAudio* fa, wxArrayString* names, wxArrayString* ids, int GetFADevices(FAudio* fa, wxArrayString* names, wxArrayString* ids,
const wxString* match) const wxString* match)
{ {
uint32_t hr;
uint32_t dev_count = 0; uint32_t dev_count = 0;
hr = FAudio_GetDeviceCount(fa, &dev_count);
if (FAudio_GetDeviceCount(fa, &dev_count) != 0) { if (hr != 0) {
wxLogError(_("FAudio: Enumerating devices failed!")); wxLogError(_("FAudio: Enumerating devices failed!"));
return true; return true;
} else { } else {
FAudioDeviceDetails dd; FAudioDeviceDetails dd;
for (uint32_t i = 0; i < dev_count; i++) { for (uint32_t i = 0; i < dev_count; i++) {
if (FAudio_GetDeviceDetails(fa, i, &dd) != 0) { hr = FAudio_GetDeviceDetails(fa, i, &dd);
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 == wxString((wchar_t*) dd.DeviceID)) } else if (*match == wxString((wchar_t*) dd.DeviceID))
return i; return i;
@ -46,13 +50,15 @@ int GetFADevices(FAudio* fa, wxArrayString* names, wxArrayString* ids,
bool GetFADevices(wxArrayString& names, wxArrayString& ids) bool GetFADevices(wxArrayString& names, wxArrayString& ids)
{ {
uint32_t hr;
FAudio* fa = NULL; FAudio* fa = NULL;
uint32_t flags = 0; uint32_t flags = 0;
#ifdef _DEBUG #ifdef _DEBUG
flags = FAUDIO_DEBUG_ENGINE; flags = FAUDIO_DEBUG_ENGINE;
#endif #endif
hr = FAudioCreate(&fa, flags, FAUDIO_DEFAULT_PROCESSOR);
if (FAudioCreate(&fa, flags, FAUDIO_DEFAULT_PROCESSOR) != 0) { if (hr != 0) {
wxLogError(_("The FAudio interface failed to initialize!")); wxLogError(_("The FAudio interface failed to initialize!"));
return false; return false;
} }
@ -78,47 +84,96 @@ static void faudio_device_changed(FAudio_Output*);
class FAudio_Device_Notifier { class FAudio_Device_Notifier {
std::wstring last_device; std::wstring last_device;
pthread_mutex_t lock;
std::vector<FAudio_Output*> instances; std::vector<FAudio_Output*> instances;
public: public:
FAudio_Device_Notifier() = default; FAudio_Device_Notifier()
~FAudio_Device_Notifier() = default; {
pthread_mutex_init(&lock, NULL);
}
~FAudio_Device_Notifier()
{
pthread_mutex_destroy(&lock);
}
ULONG FAUDIOAPI AddRef()
{
return 1;
}
ULONG FAUDIOAPI Release()
{
return 1;
}
//uint32_t FAUDIOAPI QueryInterface or it should be, I still need to find a cross platform way to query devices.
void OnDefaultDeviceChanged() // This is the callback that is called when the default device is changed. The XAudio2 driver leverages mmdeviceapi, but here we can't rely on that, so we need to find a method that work on all platforms.
{
pthread_mutex_lock(&lock);
for (auto it = instances.begin(); it < instances.end(); ++it)
{
faudio_device_changed(*it);
}
pthread_mutex_unlock(&lock);
}
void do_register(FAudio_Output* p_instance) void do_register(FAudio_Output* p_instance)
{ {
pthread_mutex_lock(&lock);
instances.push_back(p_instance); instances.push_back(p_instance);
pthread_mutex_unlock(&lock);
} }
void do_unregister(FAudio_Output* p_instance) void do_unregister(FAudio_Output* p_instance)
{ {
pthread_mutex_lock(&lock);
for (auto it = instances.begin(); it < instances.end(); ++it) { for (auto it = instances.begin(); it < instances.end(); ++it) {
if (*it == p_instance) { if (*it == p_instance) {
instances.erase(it); instances.erase(it);
break; break;
} }
} }
pthread_mutex_unlock(&lock);
} }
void OnDefaultDeviceChanged()
{
for (auto it = instances.begin(); it < instances.end(); ++it)
{
faudio_device_changed(*it);
}
}
} g_notifier; } g_notifier;
// Synchronization Event // Synchronization Event
class FAudio_BufferNotify : public FAudioVoiceCallback { class FAudio_BufferNotify : public FAudioVoiceCallback {
public: public:
void OnBufferEnd(void* pBufferContext) {} void *hBufferEndEvent;
FAudio_BufferNotify()
{
hBufferEndEvent = NULL;
//I'm still figuring out how to deal with pthreads, so I'm going to leave this empty for now.
//hBufferEndEvent = pthread_cond_init();
}
~FAudio_BufferNotify()
{
//I'm still figuring out how to deal with pthreads, so I'm going to leave this empty for now.
//pthread_cond_destroy(hBufferEndEvent);
}
void OnBufferEnd(void* pBufferContext)
{
}
void OnVoiceProcessingPassStart(uint32_t BytesRequired) {} void OnVoiceProcessingPassStart(uint32_t BytesRequired) {}
void OnVoiceProcessingPassEnd() {} void OnVoiceProcessingPassEnd() {}
void OnStreamEnd() {} void OnStreamEnd() {}
void OnBufferStart(void* pBufferContext) {} void OnBufferStart(void* pBufferContext) {}
void OnLoopEnd(void* pBufferContext) {} void OnLoopEnd(void* pBufferContext) {}
void OnVoiceError(void* pBufferContext, int Error) {} void OnVoiceError(void* pBufferContext, uint32_t Error) {}
}; };
// Class Declaration // Class Declaration
class FAudio_Output class FAudio_Output
: public SoundDriver { : public SoundDriver {
@ -226,13 +281,15 @@ bool FAudio_Output::init(long sampleRate)
if (failed || initialized) if (failed || initialized)
return false; return false;
uint32_t hr;
// Initialize FAudio // Initialize FAudio
uint32_t 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);
if (FAudioCreate(&faud, flags, FAUDIO_DEFAULT_PROCESSOR) != 0) { 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;
@ -255,8 +312,15 @@ bool FAudio_Output::init(long sampleRate)
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(faud,
&mVoice,
FAUDIO_DEFAULT_CHANNELS,
FAUDIO_DEFAULT_SAMPLERATE,
0,
FAGetDev(faud),
NULL);
if (FAudio_CreateMasteringVoice(faud, &mVoice, FAUDIO_DEFAULT_CHANNELS, FAUDIO_DEFAULT_SAMPLERATE, 0, FAGetDev(faud), NULL) != 0) { if (hr != 0) {
wxLogError(_("FAudio: Creating mastering voice failed!")); wxLogError(_("FAudio: Creating mastering voice failed!"));
failed = true; failed = true;
return false; return false;
@ -265,8 +329,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, &notify); //hr = faud->CreateSourceVoice(&sVoice, &wfx, 0, 4.0f, &notify);
hr = FAudio_CreateSourceVoice(faud, &sVoice, &wfx, 0, 4.0f, &notify);
if (FAudio_CreateSourceVoice(faud, &sVoice, (const FAudioWaveFormatEx*)&wfx, 0, 4.0f, &notify, NULL, NULL) != 0) { if (hr != 0) {
wxLogError(_("FAudio: Creating source voice failed!")); wxLogError(_("FAudio: Creating source voice failed!"));
failed = true; failed = true;
return false; return false;
@ -372,14 +437,16 @@ bool FAudio_Output::init(long sampleRate)
} }
if (matrixAvailable) { if (matrixAvailable) {
assert(FAudioVoice_SetOutputMatrix(sVoice, NULL, 2, dd.OutputFormat.Format.nChannels, matrix, FAUDIO_DEFAULT_CHANNELS) == 0); hr = FAudioVoice_SetOutputMatrix(sVoice, NULL, 2, dd.OutputFormat.Format.nChannels, matrix, FAUDIO_DEFAULT_CHANNELS);
assert(hr == 0);
} }
free(matrix); free(matrix);
matrix = NULL; matrix = NULL;
} }
assert(FAudioSourceVoice_Start(sVoice, 0, FAUDIO_COMMIT_NOW) == 0); hr = FAudioSourceVoice_Start(sVoice, 0, FAUDIO_COMMIT_NOW);
assert(hr == 0);
playing = true; playing = true;
currentBuffer = 0; currentBuffer = 0;
device_changed = false; device_changed = false;
@ -417,16 +484,16 @@ void FAudio_Output::write(uint16_t* finalWave, int length)
break; break;
} //else { } //else {
// 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 (WaitForSingleObject(notify.hBufferEndEvent, 10000) == WAIT_TIMEOUT) {
// device_changed = true; device_changed = true;
// } }
//} else { } else {
// drop current audio frame // drop current audio frame
// return; return;
//} }
//} }
} }
// copy & protect the audio data in own memory area while playing it // copy & protect the audio data in own memory area while playing it
@ -435,7 +502,8 @@ void FAudio_Output::write(uint16_t* finalWave, int length)
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
assert(FAudioSourceVoice_SubmitSourceBuffer(sVoice, &buf, NULL) == 0); uint32_t hr = FAudioSourceVoice_SubmitSourceBuffer(sVoice, &buf, NULL);
assert(hr == 0);
} }
void FAudio_Output::pause() void FAudio_Output::pause()
@ -444,7 +512,8 @@ void FAudio_Output::pause()
return; return;
if (playing) { if (playing) {
assert(FAudioSourceVoice_Stop(sVoice, 0, FAUDIO_COMMIT_NOW) == 0); uint32_t hr = FAudioSourceVoice_Stop(sVoice, 0, FAUDIO_COMMIT_NOW);
assert(hr == 0);
playing = false; playing = false;
} }
} }
@ -455,7 +524,8 @@ void FAudio_Output::resume()
return; return;
if (!playing) { if (!playing) {
assert(FAudioSourceVoice_Start(sVoice, 0, FAUDIO_COMMIT_NOW) == 0); uint32_t hr = FAudioSourceVoice_Start(sVoice, 0, FAUDIO_COMMIT_NOW);
assert(hr == 0);
playing = true; playing = true;
} }
} }
@ -466,7 +536,8 @@ void FAudio_Output::reset()
return; return;
if (playing) { if (playing) {
assert(FAudioSourceVoice_Stop(sVoice, 0, FAUDIO_COMMIT_NOW) == 0); uint32_t hr = FAudioSourceVoice_Stop(sVoice, 0, FAUDIO_COMMIT_NOW);
assert(hr == 0);
} }
FAudioSourceVoice_FlushSourceBuffers(sVoice); FAudioSourceVoice_FlushSourceBuffers(sVoice);
@ -482,7 +553,8 @@ void FAudio_Output::setThrottle(unsigned short throttle_)
if (throttle_ == 0) if (throttle_ == 0)
throttle_ = 100; throttle_ = 100;
assert(FAudioSourceVoice_SetFrequencyRatio(sVoice, (float)throttle_ / 100.0f, FAUDIO_MAX_FILTER_FREQUENCY) == 0); uint32_t hr = FAudioSourceVoice_SetFrequencyRatio(sVoice, (float)throttle_ / 100.0f, FAUDIO_MAX_FILTER_FREQUENCY);
assert(hr == 0);
} }
void faudio_device_changed(FAudio_Output* instance) void faudio_device_changed(FAudio_Output* instance)