From fd6b734fd097be49c7630b9273e73b2f25c691d5 Mon Sep 17 00:00:00 2001 From: Lior Halphon Date: Fri, 24 Jun 2022 13:27:02 +0300 Subject: [PATCH] Add XAudio2.7 as an compile-time audio driver for vanilla Windows 7 --- SDL/audio/xaudio2_7.c | 131 ++++++++++++++++++++++++++++++++++++++++++ SDL/audio/xaudio2_7.h | 103 +++++++++++++++++++++++++++++++++ 2 files changed, 234 insertions(+) create mode 100644 SDL/audio/xaudio2_7.c create mode 100644 SDL/audio/xaudio2_7.h diff --git a/SDL/audio/xaudio2_7.c b/SDL/audio/xaudio2_7.c new file mode 100644 index 0000000..eb81905 --- /dev/null +++ b/SDL/audio/xaudio2_7.c @@ -0,0 +1,131 @@ +#include "xaudio2_7.h" +#include "audio.h" + + +#define AUDIO_FREQUENCY 96000 +static IXAudio2 *xaudio2 = NULL; +static IXAudio2MasteringVoice *master_voice = NULL; +static IXAudio2SourceVoice *source_voice = NULL; +static bool playing = false; +static GB_sample_t sample_pool[0x2000]; +static unsigned pos = 0; + +#define BATCH_SIZE 256 + + +static const WAVEFORMATEX wave_format = { + .wFormatTag = WAVE_FORMAT_PCM, + .nChannels = 2, + .nSamplesPerSec = AUDIO_FREQUENCY, + .nAvgBytesPerSec = AUDIO_FREQUENCY * 4, + .nBlockAlign = 4, + .wBitsPerSample = 16, + .cbSize = 0 +}; + +static inline HRESULT XAudio2Create(IXAudio2 **ppXAudio2, + UINT32 Flags, + XAUDIO2_PROCESSOR XAudio2Processor) +{ + IXAudio2 *pXAudio2; + LoadLibraryEx("xaudio2_7.dll", NULL, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS); + + HRESULT hr = CoCreateInstance(&CLSID_XAudio2, NULL, CLSCTX_INPROC_SERVER, &IID_IXAudio2, (void**)&pXAudio2); + if (SUCCEEDED(hr)) { + hr = pXAudio2->lpVtbl->Initialize(pXAudio2, Flags, XAudio2Processor); + } + + if (SUCCEEDED(hr)) { + *ppXAudio2 = pXAudio2; + } + else { + pXAudio2->lpVtbl->Release(pXAudio2); + } + return hr; +} + +bool GB_audio_is_playing(void) +{ + return playing; +} + +void GB_audio_set_paused(bool paused) +{ + if (paused) { + playing = false; + IXAudio2SourceVoice_Stop(source_voice, 0, XAUDIO2_COMMIT_NOW); + GB_audio_clear_queue(); + } + else { + playing = true; + IXAudio2SourceVoice_Start(source_voice, 0, XAUDIO2_COMMIT_NOW); + } + +} + +void GB_audio_clear_queue(void) +{ + pos = 0; + IXAudio2SourceVoice_FlushSourceBuffers(source_voice); +} + +unsigned GB_audio_get_frequency(void) +{ + return AUDIO_FREQUENCY; +} + +size_t GB_audio_get_queue_length(void) +{ + static XAUDIO2_VOICE_STATE state; + IXAudio2SourceVoice_GetState(source_voice, &state); + + return state.BuffersQueued * BATCH_SIZE + (pos & (BATCH_SIZE - 1)); +} + +void GB_audio_queue_sample(GB_sample_t *sample) +{ + if (!playing) return; + + static XAUDIO2_BUFFER buffer = {.AudioBytes = sizeof(*sample) * BATCH_SIZE, }; + sample_pool[pos] = *sample; + buffer.pAudioData = (void *)&sample_pool[pos & ~(BATCH_SIZE - 1)]; + pos++; + pos &= 0x1fff; + if ((pos & (BATCH_SIZE - 1)) == 0) { + IXAudio2SourceVoice_SubmitSourceBuffer(source_voice, &buffer, NULL); + } +} + +void GB_audio_init(void) +{ + HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); + if (FAILED(hr)) { + fprintf(stderr, "CoInitializeEx failed: %lx\n", hr); + return; + } + + hr = XAudio2Create(&xaudio2, 0, XAUDIO2_DEFAULT_PROCESSOR); + if (FAILED(hr)) { + fprintf(stderr, "XAudio2Create failed: %lx\n", hr); + return; + } + + hr = IXAudio2_CreateMasteringVoice(xaudio2, &master_voice, + 2, // 2 channels + AUDIO_FREQUENCY, + 0, // Flags + 0, // Device index + NULL // Effect chain + ); + if (FAILED(hr)) { + fprintf(stderr, "CreateMasteringVoice failed: %lx\n", hr); + return; + } + + hr = IXAudio2_CreateSourceVoice(xaudio2, &source_voice, &wave_format, 0, XAUDIO2_DEFAULT_FREQ_RATIO, NULL, NULL, NULL); + + if (FAILED(hr)) { + fprintf(stderr, "CreateSourceVoice failed: %lx\n", hr); + return; + } +} diff --git a/SDL/audio/xaudio2_7.h b/SDL/audio/xaudio2_7.h new file mode 100644 index 0000000..c436d2e --- /dev/null +++ b/SDL/audio/xaudio2_7.h @@ -0,0 +1,103 @@ +#define INITGUID +#include + +/* Minimal definitions for XAudio2.7 */ +typedef UINT32 XAUDIO2_PROCESSOR; + +typedef struct XAUDIO2_BUFFER { + UINT32 Flags; + UINT32 AudioBytes; + const BYTE *pAudioData; + UINT32 PlayBegin; + UINT32 PlayLength; + UINT32 LoopBegin; + UINT32 LoopLength; + UINT32 LoopCount; + void *pContext; +} XAUDIO2_BUFFER; + +typedef struct XAUDIO2_VOICE_STATE { + void *pCurrentBufferContext; + UINT32 BuffersQueued; + UINT64 SamplesPlayed; +} XAUDIO2_VOICE_STATE; + +typedef struct IXAudio2SourceVoice { + struct IXAudio2SourceVoiceVtbl *lpVtbl; +} IXAudio2SourceVoice; + +typedef struct IXAudio2SourceVoiceVtbl IXAudio2SourceVoiceVtbl; + +#undef INTERFACE +#define INTERFACE IXAudio2SourceVoice + +struct IXAudio2SourceVoiceVtbl { + void *voiceMethods[19]; // Unused inherited methods + STDMETHOD(Start) (THIS_ UINT32 Flags, UINT32 OperationSet) PURE; + STDMETHOD(Stop) (THIS_ UINT32 Flags, UINT32 OperationSet) PURE; + STDMETHOD(SubmitSourceBuffer) (THIS_ __in const XAUDIO2_BUFFER *pBuffer, __in_opt const void *pBufferWMA) PURE; + STDMETHOD(FlushSourceBuffers) (THIS) PURE; + STDMETHOD(Discontinuity) (THIS) PURE; + STDMETHOD(ExitLoop) (THIS_ UINT32 OperationSet) PURE; + STDMETHOD_(void, GetState) (THIS_ __out XAUDIO2_VOICE_STATE *pVoiceState) PURE; +}; + +typedef struct IXAudio2 { + struct IXAudio2Vtbl *lpVtbl; +} IXAudio2; + +typedef struct IXAudio2Vtbl IXAudio2Vtbl; +typedef void *IXAudio2MasteringVoice; + +#undef INTERFACE +#define INTERFACE IXAudio2 + +struct IXAudio2Vtbl { + void *QueryInterface; + STDMETHOD_(ULONG, AddRef) (THIS) PURE; + STDMETHOD_(ULONG, Release) (THIS) PURE; + void *GetDeviceCount; + void *GetDeviceDetails; + STDMETHOD(Initialize) (THIS_ UINT32 Flags, + XAUDIO2_PROCESSOR XAudio2Processor) PURE; + + void *RegisterForCallbacks; + void *UnregisterForCallbacks; + + STDMETHOD(CreateSourceVoice) (THIS_ __deref_out IXAudio2SourceVoice **ppSourceVoice, + __in const WAVEFORMATEX *pSourceFormat, + UINT32 Flags, + float MaxFrequencyRatio, + __in_opt void *pCallback, + __in_opt const void *pSendList, + __in_opt const void *pEffectChain) PURE; + + void *CreateSubmixVoice; + + STDMETHOD(CreateMasteringVoice) (THIS_ __deref_out IXAudio2MasteringVoice **ppMasteringVoice, + UINT32 InputChannels, + UINT32 InputSampleRate, + UINT32 Flags, UINT32 DeviceIndex, + __in_opt const void *pEffectChain) PURE; +}; + +#define DEFINE_CLSID(className, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ +DEFINE_GUID(CLSID_##className, 0x##l, 0x##w1, 0x##w2, 0x##b1, 0x##b2, 0x##b3, 0x##b4, 0x##b5, 0x##b6, 0x##b7, 0x##b8) + +#define DEFINE_IID(interfaceName, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ +DEFINE_GUID(IID_##interfaceName, 0x##l, 0x##w1, 0x##w2, 0x##b1, 0x##b2, 0x##b3, 0x##b4, 0x##b5, 0x##b6, 0x##b7, 0x##b8) + +DEFINE_CLSID(XAudio2, 5a508685, a254, 4fba, 9b, 82, 9a, 24, b0, 03, 06, af); +DEFINE_IID(IXAudio2, 8bcf1f58, 9fe7, 4583, 8a, c6, e2, ad, c4, 65, c8, bb); + +#define IXAudio2SourceVoice_Start(This,Flags,OperationSet) ((This)->lpVtbl->Start(This,Flags,OperationSet)) +#define IXAudio2SourceVoice_Stop(This,Flags,OperationSet) ((This)->lpVtbl->Stop(This,Flags,OperationSet)) +#define IXAudio2SourceVoice_SubmitSourceBuffer(This,pBuffer,pBufferWMA) ((This)->lpVtbl->SubmitSourceBuffer(This,pBuffer,pBufferWMA)) +#define IXAudio2SourceVoice_FlushSourceBuffers(This) ((This)->lpVtbl->FlushSourceBuffers(This)) +#define IXAudio2SourceVoice_GetState(This,pVoiceState) ((This)->lpVtbl->GetState(This,pVoiceState)) +#define IXAudio2_CreateMasteringVoice(This,ppMasteringVoice,InputChannels,InputSampleRate,Flags,DeviceIndex,pEffectChain) ((This)->lpVtbl->CreateMasteringVoice(This,ppMasteringVoice,InputChannels,InputSampleRate,Flags,DeviceIndex,pEffectChain)) +#define IXAudio2_CreateSourceVoice(This,ppSourceVoice,pSourceFormat,Flags,MaxFrequencyRatio,pCallback,pSendList,pEffectChain) ((This)->lpVtbl->CreateSourceVoice(This,ppSourceVoice,pSourceFormat,Flags,MaxFrequencyRatio,pCallback,pSendList,pEffectChain)) + +#define XAUDIO2_COMMIT_NOW 0 +#define XAUDIO2_DEFAULT_PROCESSOR 0xffffffff +#define XAUDIO2_DEFAULT_FREQ_RATIO 2.0f \ No newline at end of file