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:
Zach Bacon 2023-12-23 21:33:03 -05:00
parent f46da1c525
commit af8335ee67
1 changed files with 94 additions and 98 deletions

View File

@ -8,9 +8,8 @@
#include "../common/SoundDriver.h"
// FAudio
#include <faudio.h>
#include <FAudio.h>
// MMDevice API
#include <mmdeviceapi.h>
#include <string>
#include <vector>
@ -19,30 +18,31 @@
#include "../System.h" // for systemMessage()
#include "../gba/Globals.h"
namespace {
int GetFADevices(FAudio* fa, wxArrayString* names, wxArrayString* ids,
const wxString* match)
{
HRESULT hr;
UINT32 dev_count = 0;
uint32_t hr;
uint32_t dev_count = 0;
hr = FAudio_GetDeviceCount(fa, &dev_count);
if (hr != S_OK) {
if (hr != 0) {
wxLogError(_("FAudio: Enumerating devices failed!"));
return true;
return -1;
} else {
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);
if (hr != S_OK) {
if (hr != 0) {
continue;
} else {
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);
} else if (*match == dd.DeviceID)
} else if (*match == wxString((wchar_t*) dd.DeviceID))
return i;
}
}
@ -50,25 +50,24 @@ int GetFADevices(FAudio* fa, wxArrayString* names, wxArrayString* ids,
return -1;
}
}
bool GetFADevices(wxArrayString& names, wxArrayString& ids)
{
HRESULT hr;
uint32_t hr;
FAudio* fa = NULL;
UINT32 flags = 0;
uint32_t flags = 0;
#ifdef _DEBUG
flags = FAUDIO_DEBUG_ENGINE;
#endif
hr = FAudioCreate(&fa, flags, FAUDIO_DEFAULT_PROCESSOR);
hr = FAudioCreate(&fa, flags, FAUDIO_DEFAULT_PROCESSOR); //Apparently this needs 3 parameters, the processor.
if (hr != S_OK) {
if (hr != 0) {
wxLogError(_("The FAudio interface failed to initialize!"));
return false;
}
GetFADevices(fa, &names, &ids, NULL);
//fa->Release();
FAudio_Release(fa);
return true;
}
@ -194,41 +193,42 @@ public:
// Synchronization Event
class FAudio_BufferNotify : public FAudioVoiceCallback {
public:
HANDLE hBufferEndEvent;
void *hBufferEndEvent;
FAudio_BufferNotify()
{
hBufferEndEvent = NULL;
hBufferEndEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
assert(hBufferEndEvent != NULL);
}
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()
{
CloseHandle(hBufferEndEvent);
hBufferEndEvent = NULL;
}
STDMETHOD_(void, OnBufferEnd)
(void* pBufferContext)
{
assert(hBufferEndEvent != NULL);
SetEvent(hBufferEndEvent);
}
// dummies:
STDMETHOD_(void, OnVoiceProcessingPassStart)
(UINT32 BytesRequired) {}
STDMETHOD_(void, OnVoiceProcessingPassEnd)
() {}
STDMETHOD_(void, OnStreamEnd)
() {}
STDMETHOD_(void, OnBufferStart)
(void* pBufferContext) {}
STDMETHOD_(void, OnLoopEnd)
(void* pBufferContext) {}
STDMETHOD_(void, OnVoiceError)
(void* pBufferContext, HRESULT Error){};
static void StaticOnBufferEnd(FAudioVoiceCallback* callback, void * pBufferContext) {
FAudio_BufferNotify* self = static_cast<FAudio_BufferNotify*>(callback);
if (self != nullptr && self->hBufferEndEvent != NULL)
{
SetEvent(self->hBufferEndEvent);
}
}
static void StaticOnVoiceProcessingPassStart(FAudioVoiceCallback* callback, uint32_t BytesRequired) {}
static void StaticOnVoiceProcessingPassEnd(FAudioVoiceCallback* callback) {}
static void StaticOnStreamEnd(FAudioVoiceCallback* callback) {}
static void StaticOnBufferStart(FAudioVoiceCallback* callback, void * pBufferContext) {}
static void StaticOnLoopEnd(FAudioVoiceCallback* callback, void * pBufferContext) {}
static void StaticOnVoiceError(FAudioVoiceCallback* callback, void * pBufferContext, uint32_t Error) {}
};
// Class Declaration
@ -258,9 +258,9 @@ private:
bool failed;
bool initialized;
bool playing;
UINT32 freq;
UINT32 bufferCount;
BYTE* buffers;
uint32_t freq;
uint32_t bufferCount;
uint8_t* buffers;
int currentBuffer;
int soundBufferLen;
@ -288,8 +288,8 @@ FAudio_Output::FAudio_Output()
faud = NULL;
mVoice = NULL;
sVoice = NULL;
ZeroMemory(&buf, sizeof(buf));
ZeroMemory(&vState, sizeof(vState));
memset(&buf, NULL, sizeof(buf));
memset(&vState, NULL, sizeof(vState));
g_notifier.do_register(this);
}
@ -305,8 +305,7 @@ void FAudio_Output::close()
if (sVoice) {
if (playing) {
HRESULT hr = FAudioSourceVoice_Stop(sVoice, 0, FAUDIO_COMMIT_NOW);
assert(hr == S_OK);
assert(FAudioSourceVoice_Stop(sVoice, 0, FAUDIO_COMMIT_NOW) == 0);
}
FAudioVoice_DestroyVoice(sVoice);
@ -339,15 +338,15 @@ bool FAudio_Output::init(long sampleRate)
if (failed || initialized)
return false;
HRESULT hr;
uint32_t hr;
// Initialize FAudio
UINT32 flags = 0;
uint32_t flags = 0;
//#ifdef _DEBUG
// flags = FAUDIO_DEBUG_ENGINE;
//#endif
hr = FAudioCreate(&faud, flags, FAUDIO_DEFAULT_PROCESSOR);
if (hr != S_OK) {
if (hr != 0) {
wxLogError(_("The FAudio interface failed to initialize!"));
failed = true;
return false;
@ -359,19 +358,18 @@ bool FAudio_Output::init(long sampleRate)
soundBufferLen = (freq / 60) * 4;
// create own buffers to store sound data because it must not be
// 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
WAVEFORMATEX wfx;
ZeroMemory(&wfx, sizeof(wfx));
wfx.wFormatTag = WAVE_FORMAT_PCM;
FAudioWaveFormatEx wfx;
memset(&wfx, NULL, sizeof(wfx));
wfx.wFormatTag = FAUDIO_FORMAT_PCM;
wfx.nChannels = 2;
wfx.nSamplesPerSec = freq;
wfx.wBitsPerSample = 16;
wfx.nBlockAlign = wfx.nChannels * (wfx.wBitsPerSample / 8);
wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
// create sound receiver
hr = FAudio_CreateMasteringVoice(
faud,
hr = FAudio_CreateMasteringVoice(faud,
&mVoice,
FAUDIO_DEFAULT_CHANNELS,
FAUDIO_DEFAULT_SAMPLERATE,
@ -379,7 +377,7 @@ bool FAudio_Output::init(long sampleRate)
FAGetDev(faud),
NULL);
if (hr != S_OK) {
if (hr != 0) {
wxLogError(_("FAudio: Creating mastering voice failed!"));
failed = true;
return false;
@ -387,10 +385,9 @@ bool FAudio_Output::init(long sampleRate)
// create sound emitter
//This should be FAudio_CreateSourceVoice()
//hr = faud->CreateSourceVoice(&sVoice, &wfx, 0, 4.0f, &notify);
hr = FAudio_CreateSourceVoice(faud, &sVoice, (const FAudioWaveFormatEx*)&wfx, 0, 4.0f, &notify, NULL, NULL);
hr = FAudio_CreateSourceVoice(faud, &sVoice, &wfx, 0, 4.0f, &notify, NULL, NULL);
if (hr != S_OK) {
if (hr != 0) {
wxLogError(_("FAudio: Creating source voice failed!"));
failed = true;
return false;
@ -399,9 +396,8 @@ bool FAudio_Output::init(long sampleRate)
if (gopts.upmix) {
// set up stereo upmixing
FAudioDeviceDetails dd;
ZeroMemory(&dd, sizeof(dd));
hr = FAudio_GetDeviceDetails(faud, 0, &dd);
assert(hr == S_OK);
memset(&dd, NULL, sizeof(dd));
assert(FAudio_GetDeviceDetails(faud, 0, &dd) == 0);
float* matrix = NULL;
matrix = (float*)malloc(sizeof(float) * 2 * dd.OutputFormat.Format.nChannels);
@ -497,8 +493,8 @@ bool FAudio_Output::init(long sampleRate)
}
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...
assert(hr == S_OK);
hr = FAudioVoice_SetOutputMatrix(sVoice, NULL, 2, dd.OutputFormat.Format.nChannels, matrix, FAUDIO_DEFAULT_CHANNELS);
assert(hr == 0);
}
free(matrix);
@ -506,7 +502,7 @@ bool FAudio_Output::init(long sampleRate)
}
hr = FAudioSourceVoice_Start(sVoice, 0, FAUDIO_COMMIT_NOW);
assert(hr == S_OK);
assert(hr == 0);
playing = true;
currentBuffer = 0;
device_changed = false;
@ -516,7 +512,7 @@ bool FAudio_Output::init(long sampleRate)
void FAudio_Output::write(uint16_t* finalWave, int length)
{
UINT32 flags = 0;
uint32_t flags = 0;
if (!initialized || failed)
return;
@ -557,13 +553,13 @@ void FAudio_Output::write(uint16_t* finalWave, int length)
}
// 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.pAudioData = &buffers[currentBuffer * soundBufferLen];
currentBuffer++;
currentBuffer %= (bufferCount + 1); // + 1 because we need one temporary buffer
HRESULT hr = FAudioSourceVoice_SubmitSourceBuffer(sVoice, &buf, NULL); // send buffer to queue.
assert(hr == S_OK);
uint32_t hr = FAudioSourceVoice_SubmitSourceBuffer(sVoice, &buf, NULL);
assert(hr == 0);
}
void FAudio_Output::pause()
@ -572,8 +568,8 @@ void FAudio_Output::pause()
return;
if (playing) {
HRESULT hr = FAudioSourceVoice_Stop(sVoice, 0, FAUDIO_COMMIT_NOW);
assert(hr == S_OK);
uint32_t hr = FAudioSourceVoice_Stop(sVoice, 0, FAUDIO_COMMIT_NOW);
assert(hr == 0);
playing = false;
}
}
@ -584,8 +580,8 @@ void FAudio_Output::resume()
return;
if (!playing) {
HRESULT hr = FAudioSourceVoice_Start(sVoice, 0, FAUDIO_COMMIT_NOW);
assert(hr == S_OK);
uint32_t hr = FAudioSourceVoice_Start(sVoice, 0, FAUDIO_COMMIT_NOW);
assert(hr == 0);
playing = true;
}
}
@ -596,8 +592,8 @@ void FAudio_Output::reset()
return;
if (playing) {
HRESULT hr = FAudioSourceVoice_Stop(sVoice, 0, FAUDIO_COMMIT_NOW);
assert(hr == S_OK);
uint32_t hr = FAudioSourceVoice_Stop(sVoice, 0, FAUDIO_COMMIT_NOW);
assert(hr == 0);
}
FAudioSourceVoice_FlushSourceBuffers(sVoice);
@ -613,8 +609,8 @@ void FAudio_Output::setThrottle(unsigned short throttle_)
if (throttle_ == 0)
throttle_ = 100;
HRESULT hr = FAudioSourceVoice_SetFrequencyRatio(sVoice, (float)throttle_ / 100.0f, FAUDIO_MAX_FILTER_FREQUENCY);
assert(hr == S_OK);
uint32_t hr = FAudioSourceVoice_SetFrequencyRatio(sVoice, (float)throttle_ / 100.0f, FAUDIO_MAX_FILTER_FREQUENCY);
assert(hr == 0);
}
void faudio_device_changed(FAudio_Output* instance)