summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTriang3l <triang3l@yandex.ru>2019-11-02 22:27:38 +0300
committerTriang3l <triang3l@yandex.ru>2019-11-02 22:27:38 +0300
commit1bb3cd45caa95f0e526120a352a6ceae351920c5 (patch)
tree4d0776d7e40ecf3c894c7e092770579d73718004
parent443e6ed72915e1045cb090a1d3795739d07985bd (diff)
[APU] Support XAudio 2.7
-rw-r--r--src/xenia/apu/xaudio2/xaudio2_api.h315
-rw-r--r--src/xenia/apu/xaudio2/xaudio2_audio_driver.cc216
-rw-r--r--src/xenia/apu/xaudio2/xaudio2_audio_driver.h24
-rw-r--r--src/xenia/apu/xaudio2/xaudio2_audio_system.cc2
4 files changed, 470 insertions, 87 deletions
diff --git a/src/xenia/apu/xaudio2/xaudio2_api.h b/src/xenia/apu/xaudio2/xaudio2_api.h
new file mode 100644
index 000000000..00c42349d
--- /dev/null
+++ b/src/xenia/apu/xaudio2/xaudio2_api.h
@@ -0,0 +1,315 @@
+/**
+ ******************************************************************************
+ * Xenia : Xbox 360 Emulator Research Project *
+ ******************************************************************************
+ * Copyright 2019 Ben Vanik. All rights reserved. *
+ * Released under the BSD license - see LICENSE in the root for more details. *
+ ******************************************************************************
+ */
+
+#ifndef XENIA_APU_XAUDIO2_XAUDIO2_API_H_
+#define XENIA_APU_XAUDIO2_XAUDIO2_API_H_
+
+#include <Audioclient.h>
+
+#include "xenia/base/platform_win.h"
+
+namespace xe {
+namespace apu {
+namespace xaudio2 {
+namespace api {
+
+// Parts of XAudio2.h needed for Xenia. The header in the Windows SDK (for 2.8+)
+// or the DirectX SDK (for 2.7 and below) is for a single version that is chosen
+// for the target OS version at compile time, and cannot be useful for different
+// DLL versions.
+//
+// XAudio 2.8 is also not available on Windows 7, but including the 2.7 header
+// is complicated because it has the same name and include guard as the 2.8 one,
+// and also 2.7 is outdated - support already dropped in Microsoft Store apps,
+// for instance.
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+
+class __declspec(uuid("5a508685-a254-4fba-9b82-9a24b00306af")) XAudio2_7;
+interface __declspec(uuid("8bcf1f58-9fe7-4583-8ac6-e2adc465c8bb")) IXAudio2_7;
+interface __declspec(uuid("60d8dac8-5aa1-4e8e-b597-2f5e2883d484")) IXAudio2_8;
+
+#pragma pack(push, 1)
+
+static constexpr UINT32 XE_XAUDIO2_MAX_QUEUED_BUFFERS = 64;
+static constexpr float XE_XAUDIO2_MAX_FREQ_RATIO = 1024.0f;
+static constexpr float XE_XAUDIO2_DEFAULT_FREQ_RATIO = 2.0f;
+
+static constexpr UINT32 XE_XAUDIO2_COMMIT_NOW = 0;
+static constexpr UINT32 XE_XAUDIO2_NO_LOOP_REGION = 0;
+static constexpr UINT32 XE_XAUDIO2_DEFAULT_CHANNELS = 0;
+static constexpr UINT32 XE_XAUDIO2_DEFAULT_SAMPLERATE = 0;
+
+// 2.8+ only.
+static constexpr UINT32 XE_XAUDIO2_VOICE_NOSAMPLESPLAYED = 0x0100;
+
+interface IXAudio2_7;
+interface IXAudio2_8;
+interface IXAudio2Voice;
+interface IXAudio2_7SourceVoice;
+interface IXAudio2_8SourceVoice;
+interface IXAudio2SubmixVoice;
+interface IXAudio2_7MasteringVoice;
+interface IXAudio2_8MasteringVoice;
+interface IXAudio2EngineCallback;
+interface IXAudio2VoiceCallback;
+
+typedef UINT32 XAUDIO2_PROCESSOR;
+static constexpr XAUDIO2_PROCESSOR XE_XAUDIO2_ANY_PROCESSOR = 0xFFFFFFFF;
+static constexpr XAUDIO2_PROCESSOR XE_XAUDIO2_7_DEFAULT_PROCESSOR =
+ XE_XAUDIO2_ANY_PROCESSOR;
+static constexpr XAUDIO2_PROCESSOR XE_XAUDIO2_8_DEFAULT_PROCESSOR = 0x00000001;
+
+// 2.7 only.
+struct XAUDIO2_DEVICE_DETAILS;
+
+struct XAUDIO2_VOICE_DETAILS;
+struct XAUDIO2_VOICE_SENDS;
+struct XAUDIO2_EFFECT_CHAIN;
+struct XAUDIO2_FILTER_PARAMETERS;
+
+struct XAUDIO2_BUFFER {
+ UINT32 Flags;
+ UINT32 AudioBytes;
+ const BYTE* pAudioData;
+ UINT32 PlayBegin;
+ UINT32 PlayLength;
+ UINT32 LoopBegin;
+ UINT32 LoopLength;
+ UINT32 LoopCount;
+ void* pContext;
+};
+
+struct XAUDIO2_BUFFER_WMA;
+
+struct XAUDIO2_VOICE_STATE {
+ void* pCurrentBufferContext;
+ UINT32 BuffersQueued;
+ UINT64 SamplesPlayed;
+};
+
+struct XAUDIO2_PERFORMANCE_DATA;
+
+struct XAUDIO2_DEBUG_CONFIGURATION {
+ UINT32 TraceMask;
+ UINT32 BreakMask;
+ BOOL LogThreadID;
+ BOOL LogFileline;
+ BOOL LogFunctionName;
+ BOOL LogTiming;
+};
+
+static constexpr UINT32 XE_XAUDIO2_LOG_ERRORS = 0x0001;
+static constexpr UINT32 XE_XAUDIO2_LOG_WARNINGS = 0x0002;
+
+// clang-format off
+
+// IXAudio2 2.7.
+DECLARE_INTERFACE_(IXAudio2_7, IUnknown) {
+ STDMETHOD(QueryInterface)(REFIID riid, void** ppvInterface) = 0;
+ STDMETHOD_(ULONG, AddRef)() = 0;
+ STDMETHOD_(ULONG, Release)() = 0;
+ // 2.7 only.
+ STDMETHOD(GetDeviceCount)(UINT32* pCount) = 0;
+ // 2.7 only.
+ STDMETHOD(GetDeviceDetails)(UINT32 Index,
+ XAUDIO2_DEVICE_DETAILS* pDeviceDetails) = 0;
+ // 2.7 only.
+ STDMETHOD(Initialize)(
+ UINT32 Flags = 0,
+ XAUDIO2_PROCESSOR XAudio2Processor = XE_XAUDIO2_7_DEFAULT_PROCESSOR) = 0;
+ STDMETHOD(RegisterForCallbacks)(IXAudio2EngineCallback* pCallback) = 0;
+ STDMETHOD_(void, UnregisterForCallbacks)(
+ IXAudio2EngineCallback* pCallback) = 0;
+ STDMETHOD(CreateSourceVoice)(
+ IXAudio2_7SourceVoice** ppSourceVoice, const WAVEFORMATEX* pSourceFormat,
+ UINT32 Flags = 0, float MaxFrequencyRatio = XE_XAUDIO2_DEFAULT_FREQ_RATIO,
+ IXAudio2VoiceCallback* pCallback = nullptr,
+ const XAUDIO2_VOICE_SENDS* pSendList = nullptr,
+ const XAUDIO2_EFFECT_CHAIN* pEffectChain = nullptr) = 0;
+ STDMETHOD(CreateSubmixVoice)(
+ IXAudio2SubmixVoice** ppSubmixVoice, UINT32 InputChannels,
+ UINT32 InputSampleRate, UINT32 Flags = 0, UINT32 ProcessingStage = 0,
+ const XAUDIO2_VOICE_SENDS* pSendList = nullptr,
+ const XAUDIO2_EFFECT_CHAIN* pEffectChain = nullptr) = 0;
+ // 2.7: Device index instead of device ID, no stream category.
+ STDMETHOD(CreateMasteringVoice)(
+ IXAudio2_7MasteringVoice** ppMasteringVoice,
+ UINT32 InputChannels = XE_XAUDIO2_DEFAULT_CHANNELS,
+ UINT32 InputSampleRate = XE_XAUDIO2_DEFAULT_SAMPLERATE, UINT32 Flags = 0,
+ UINT32 DeviceIndex = 0,
+ const XAUDIO2_EFFECT_CHAIN* pEffectChain = nullptr) = 0;
+ STDMETHOD(StartEngine)() = 0;
+ STDMETHOD_(void, StopEngine)() = 0;
+ STDMETHOD(CommitChanges)(UINT32 OperationSet) = 0;
+ STDMETHOD_(void, GetPerformanceData)(XAUDIO2_PERFORMANCE_DATA* pPerfData) = 0;
+ STDMETHOD_(void, SetDebugConfiguration)(
+ const XAUDIO2_DEBUG_CONFIGURATION* pDebugConfiguration,
+ void* pReserved = nullptr) = 0;
+};
+
+// IXAudio2 2.8.
+DECLARE_INTERFACE_(IXAudio2_8, IUnknown) {
+ STDMETHOD(QueryInterface)(REFIID riid, void** ppvInterface) = 0;
+ STDMETHOD_(ULONG, AddRef)() = 0;
+ STDMETHOD_(ULONG, Release)() = 0;
+ // 2.8: No GetDeviceCount, GetDeviceDetails and Initialize.
+ STDMETHOD(RegisterForCallbacks)(IXAudio2EngineCallback* pCallback) = 0;
+ STDMETHOD_(void, UnregisterForCallbacks)(
+ IXAudio2EngineCallback* pCallback) = 0;
+ STDMETHOD(CreateSourceVoice)(
+ IXAudio2_8SourceVoice** ppSourceVoice, const WAVEFORMATEX* pSourceFormat,
+ UINT32 Flags = 0, float MaxFrequencyRatio = XE_XAUDIO2_DEFAULT_FREQ_RATIO,
+ IXAudio2VoiceCallback* pCallback = nullptr,
+ const XAUDIO2_VOICE_SENDS* pSendList = nullptr,
+ const XAUDIO2_EFFECT_CHAIN* pEffectChain = nullptr) = 0;
+ STDMETHOD(CreateSubmixVoice)(
+ IXAudio2SubmixVoice** ppSubmixVoice, UINT32 InputChannels,
+ UINT32 InputSampleRate, UINT32 Flags = 0, UINT32 ProcessingStage = 0,
+ const XAUDIO2_VOICE_SENDS* pSendList = nullptr,
+ const XAUDIO2_EFFECT_CHAIN* pEffectChain = nullptr) = 0;
+ // 2.8: Device ID instead of device index, added stream category.
+ STDMETHOD(CreateMasteringVoice)(
+ IXAudio2_8MasteringVoice** ppMasteringVoice,
+ UINT32 InputChannels = XE_XAUDIO2_DEFAULT_CHANNELS,
+ UINT32 InputSampleRate = XE_XAUDIO2_DEFAULT_SAMPLERATE, UINT32 Flags = 0,
+ LPCWSTR szDeviceId = nullptr,
+ const XAUDIO2_EFFECT_CHAIN* pEffectChain = nullptr,
+ AUDIO_STREAM_CATEGORY StreamCategory = AudioCategory_GameEffects) = 0;
+ STDMETHOD(StartEngine)() = 0;
+ STDMETHOD_(void, StopEngine)() = 0;
+ STDMETHOD(CommitChanges)(UINT32 OperationSet) = 0;
+ STDMETHOD_(void, GetPerformanceData)(XAUDIO2_PERFORMANCE_DATA* pPerfData) = 0;
+ STDMETHOD_(void, SetDebugConfiguration)(
+ const XAUDIO2_DEBUG_CONFIGURATION* pDebugConfiguration,
+ void* pReserved = nullptr) = 0;
+};
+
+DECLARE_INTERFACE(IXAudio2Voice) {
+#define XE_APU_XAUDIO2_API_DECLARE_IXAUDIO2VOICE_METHODS \
+ STDMETHOD_(void, GetVoiceDetails)(XAUDIO2_VOICE_DETAILS* pVoiceDetails) = 0; \
+ STDMETHOD(SetOutputVoices)(const XAUDIO2_VOICE_SENDS* pSendList) = 0; \
+ STDMETHOD(SetEffectChain)(const XAUDIO2_EFFECT_CHAIN* pEffectChain) = 0; \
+ STDMETHOD(EnableEffect)(UINT32 EffectIndex, \
+ UINT32 OperationSet = XE_XAUDIO2_COMMIT_NOW) = 0; \
+ STDMETHOD(DisableEffect)(UINT32 EffectIndex, \
+ UINT32 OperationSet = XE_XAUDIO2_COMMIT_NOW) = 0; \
+ STDMETHOD_(void, GetEffectState)(UINT32 EffectIndex, BOOL* pEnabled) = 0; \
+ STDMETHOD(SetEffectParameters)( \
+ UINT32 EffectIndex, const void* pParameters, UINT32 ParametersByteSize, \
+ UINT32 OperationSet = XE_XAUDIO2_COMMIT_NOW) = 0; \
+ STDMETHOD(GetEffectParameters)(UINT32 EffectIndex, void* pParameters, \
+ UINT32 ParametersByteSize) = 0; \
+ STDMETHOD(SetFilterParameters)( \
+ const XAUDIO2_FILTER_PARAMETERS* pParameters, \
+ UINT32 OperationSet = XE_XAUDIO2_COMMIT_NOW) = 0; \
+ STDMETHOD_(void, GetFilterParameters)( \
+ XAUDIO2_FILTER_PARAMETERS* pParameters) = 0; \
+ STDMETHOD(SetOutputFilterParameters)( \
+ IXAudio2Voice* pDestinationVoice, \
+ const XAUDIO2_FILTER_PARAMETERS* pParameters, \
+ UINT32 OperationSet = XE_XAUDIO2_COMMIT_NOW) = 0; \
+ STDMETHOD_(void, GetOutputFilterParameters)( \
+ IXAudio2Voice* pDestinationVoice, \
+ XAUDIO2_FILTER_PARAMETERS* pParameters) = 0; \
+ STDMETHOD(SetVolume)(float Volume, \
+ UINT32 OperationSet = XE_XAUDIO2_COMMIT_NOW) = 0; \
+ STDMETHOD_(void, GetVolume)(float* pVolume) = 0; \
+ STDMETHOD(SetChannelVolumes)( \
+ UINT32 Channels, const float* pVolumes, \
+ UINT32 OperationSet = XE_XAUDIO2_COMMIT_NOW) = 0; \
+ STDMETHOD_(void, GetChannelVolumes)(UINT32 Channels, float* pVolumes) = 0; \
+ STDMETHOD(SetOutputMatrix)(IXAudio2Voice* pDestinationVoice, \
+ UINT32 SourceChannels, \
+ UINT32 DestinationChannels, \
+ const float* pLevelMatrix, \
+ UINT32 OperationSet = XE_XAUDIO2_COMMIT_NOW) = 0; \
+ STDMETHOD_(void, GetOutputMatrix)(IXAudio2Voice* pDestinationVoice, \
+ UINT32 SourceChannels, \
+ UINT32 DestinationChannels, \
+ float* pLevelMatrix) = 0; \
+ STDMETHOD_(void, DestroyVoice)() = 0;
+ XE_APU_XAUDIO2_API_DECLARE_IXAUDIO2VOICE_METHODS
+};
+
+// IXAudio2SourceVoice 2.7.
+DECLARE_INTERFACE_(IXAudio2_7SourceVoice, IXAudio2Voice) {
+ XE_APU_XAUDIO2_API_DECLARE_IXAUDIO2VOICE_METHODS
+ STDMETHOD(Start)(UINT32 Flags = 0,
+ UINT32 OperationSet = XE_XAUDIO2_COMMIT_NOW) = 0;
+ STDMETHOD(Stop)(UINT32 Flags = 0,
+ UINT32 OperationSet = XE_XAUDIO2_COMMIT_NOW) = 0;
+ STDMETHOD(SubmitSourceBuffer)(
+ const XAUDIO2_BUFFER* pBuffer,
+ const XAUDIO2_BUFFER_WMA* pBufferWMA = nullptr) = 0;
+ STDMETHOD(FlushSourceBuffers)() = 0;
+ STDMETHOD(Discontinuity)() = 0;
+ STDMETHOD(ExitLoop)(UINT32 OperationSet = XE_XAUDIO2_COMMIT_NOW) = 0;
+ // 2.7: No Flags.
+ STDMETHOD_(void, GetState)(XAUDIO2_VOICE_STATE* pVoiceState) = 0;
+ STDMETHOD(SetFrequencyRatio)(float Ratio,
+ UINT32 OperationSet = XE_XAUDIO2_COMMIT_NOW) = 0;
+ STDMETHOD_(void, GetFrequencyRatio)(float* pRatio) = 0;
+ STDMETHOD(SetSourceSampleRate)(UINT32 NewSourceSampleRate) = 0;
+};
+
+// IXAudio2SourceVoice 2.8.
+DECLARE_INTERFACE_(IXAudio2_8SourceVoice, IXAudio2Voice) {
+ XE_APU_XAUDIO2_API_DECLARE_IXAUDIO2VOICE_METHODS
+ STDMETHOD(Start)(UINT32 Flags = 0,
+ UINT32 OperationSet = XE_XAUDIO2_COMMIT_NOW) = 0;
+ STDMETHOD(Stop)(UINT32 Flags = 0,
+ UINT32 OperationSet = XE_XAUDIO2_COMMIT_NOW) = 0;
+ STDMETHOD(SubmitSourceBuffer)(
+ const XAUDIO2_BUFFER* pBuffer,
+ const XAUDIO2_BUFFER_WMA* pBufferWMA = nullptr) = 0;
+ STDMETHOD(FlushSourceBuffers)() = 0;
+ STDMETHOD(Discontinuity)() = 0;
+ STDMETHOD(ExitLoop)(UINT32 OperationSet = XE_XAUDIO2_COMMIT_NOW) = 0;
+ // 2.8: Flags added.
+ STDMETHOD_(void, GetState)(XAUDIO2_VOICE_STATE* pVoiceState,
+ UINT32 Flags = 0) = 0;
+ STDMETHOD(SetFrequencyRatio)(float Ratio,
+ UINT32 OperationSet = XE_XAUDIO2_COMMIT_NOW) = 0;
+ STDMETHOD_(void, GetFrequencyRatio)(float* pRatio) = 0;
+ STDMETHOD(SetSourceSampleRate)(UINT32 NewSourceSampleRate) = 0;
+};
+
+// IXAudio2MasteringVoice 2.7.
+DECLARE_INTERFACE_(IXAudio2_7MasteringVoice, IXAudio2Voice) {
+ XE_APU_XAUDIO2_API_DECLARE_IXAUDIO2VOICE_METHODS
+ // 2.7: No GetChannelMask.
+};
+
+// IXAudio2MasteringVoice 2.8.
+DECLARE_INTERFACE_(IXAudio2_8MasteringVoice, IXAudio2Voice) {
+ XE_APU_XAUDIO2_API_DECLARE_IXAUDIO2VOICE_METHODS
+ // 2.8: GetChannelMask added.
+ STDMETHOD(GetChannelMask)(DWORD* pChannelmask) = 0;
+};
+
+DECLARE_INTERFACE(IXAudio2VoiceCallback) {
+ // Called just before this voice's processing pass begins.
+ STDMETHOD_(void, OnVoiceProcessingPassStart)(UINT32 BytesRequired) = 0;
+ STDMETHOD_(void, OnVoiceProcessingPassEnd)() = 0;
+ STDMETHOD_(void, OnStreamEnd)() = 0;
+ STDMETHOD_(void, OnBufferStart)(void* pBufferContext) = 0;
+ STDMETHOD_(void, OnBufferEnd)(void* pBufferContext) = 0;
+ STDMETHOD_(void, OnLoopEnd)(void* pBufferContext) = 0;
+ STDMETHOD_(void, OnVoiceError)(void* pBufferContext, HRESULT Error) = 0;
+};
+
+#pragma pack(pop)
+
+} // namespace api
+} // namespace xaudio2
+} // namespace apu
+} // namespace xe
+
+#endif // XENIA_APU_XAUDIO2_XAUDIO2_API_H_
diff --git a/src/xenia/apu/xaudio2/xaudio2_audio_driver.cc b/src/xenia/apu/xaudio2/xaudio2_audio_driver.cc
index fe3f8bd10..1f5d2ffc8 100644
--- a/src/xenia/apu/xaudio2/xaudio2_audio_driver.cc
+++ b/src/xenia/apu/xaudio2/xaudio2_audio_driver.cc
@@ -12,8 +12,6 @@
// Must be included before xaudio2.h so we get the right windows.h include.
#include "xenia/base/platform_win.h"
-#include <xaudio2.h> // NOLINT(build/include_order)
-
#include "xenia/apu/apu_flags.h"
#include "xenia/base/clock.h"
#include "xenia/base/logging.h"
@@ -22,7 +20,7 @@ namespace xe {
namespace apu {
namespace xaudio2 {
-class XAudio2AudioDriver::VoiceCallback : public IXAudio2VoiceCallback {
+class XAudio2AudioDriver::VoiceCallback : public api::IXAudio2VoiceCallback {
public:
explicit VoiceCallback(xe::threading::Semaphore* semaphore)
: semaphore_(semaphore) {}
@@ -45,74 +43,97 @@ class XAudio2AudioDriver::VoiceCallback : public IXAudio2VoiceCallback {
XAudio2AudioDriver::XAudio2AudioDriver(Memory* memory,
xe::threading::Semaphore* semaphore)
- : AudioDriver(memory), semaphore_(semaphore) {
- static_assert(frame_count_ == XAUDIO2_MAX_QUEUED_BUFFERS,
- "xaudio header differs");
-}
+ : AudioDriver(memory), semaphore_(semaphore) {}
XAudio2AudioDriver::~XAudio2AudioDriver() = default;
-const DWORD ChannelMasks[] = {
- 0,
- 0,
- SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_LOW_FREQUENCY,
- 0,
- 0,
- 0,
- SPEAKER_FRONT_LEFT | SPEAKER_FRONT_CENTER | SPEAKER_FRONT_RIGHT |
- SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT,
- 0,
-};
-
bool XAudio2AudioDriver::Initialize() {
HRESULT hr;
voice_callback_ = new VoiceCallback(semaphore_);
- // Load XAudio2_8.dll dynamically - Windows 8.1 SDK references XAudio2_8.dll
- // in xaudio2.lib, which is available on Windows 8.1, however, Windows 10 SDK
- // references XAudio2_9.dll in it, which is only available in Windows 10, and
- // XAudio2_8.dll is linked through a different .lib - xaudio2_8.lib, so easier
- // not to link the .lib at all.
+ // Load the XAudio2 DLL dynamically. Needed both for 2.7 and for
+ // differentiating between 2.8 and later versions. Windows 8.1 SDK references
+ // XAudio2_8.dll in xaudio2.lib, which is available on Windows 8.1, however,
+ // Windows 10 SDK references XAudio2_9.dll in it, which is only available in
+ // Windows 10, and XAudio2_8.dll is linked through a different .lib -
+ // xaudio2_8.lib, so easier not to link the .lib at all.
xaudio2_module_ = reinterpret_cast<void*>(LoadLibrary(L"XAudio2_8.dll"));
- if (!xaudio2_module_) {
- XELOGE("LoadLibrary(XAudio2_8.dll) failed");
- assert_always();
- return false;
+ if (xaudio2_module_) {
+ api_minor_version_ = 8;
+ } else {
+ xaudio2_module_ = reinterpret_cast<void*>(LoadLibrary(L"XAudio2_7.dll"));
+ if (xaudio2_module_) {
+ api_minor_version_ = 7;
+ } else {
+ XELOGE("Failed to load XAudio 2.8 or 2.7 library DLL");
+ assert_always();
+ return false;
+ }
}
- union {
- HRESULT(__stdcall* xaudio2_create)
- (IXAudio2** xaudio2_out, UINT32 flags, XAUDIO2_PROCESSOR xaudio2_processor);
- FARPROC xaudio2_create_ptr;
- };
- xaudio2_create_ptr = GetProcAddress(
- reinterpret_cast<HMODULE>(xaudio2_module_), "XAudio2Create");
- if (!xaudio2_create_ptr) {
- XELOGE("GetProcAddress(XAudio2_8.dll, XAudio2Create) failed");
- assert_always();
- return false;
+ // First CPU (2.8 default). XAUDIO2_ANY_PROCESSOR (2.7 default) steals too
+ // much time from other things. Ideally should process audio on what roughly
+ // represents thread 4 (5th) on the Xbox 360 (2.7 default on the console), or
+ // even beyond the 6 guest cores.
+ api::XAUDIO2_PROCESSOR processor = 0x00000001;
+ if (api_minor_version_ >= 8) {
+ union {
+ // clang-format off
+ HRESULT (__stdcall* xaudio2_create)(
+ api::IXAudio2_8** xaudio2_out, UINT32 flags,
+ api::XAUDIO2_PROCESSOR xaudio2_processor);
+ // clang-format on
+ FARPROC xaudio2_create_ptr;
+ };
+ xaudio2_create_ptr = GetProcAddress(
+ reinterpret_cast<HMODULE>(xaudio2_module_), "XAudio2Create");
+ if (!xaudio2_create_ptr) {
+ XELOGE("XAudio2Create not found in XAudio2_8.dll");
+ assert_always();
+ return false;
+ }
+ hr = xaudio2_create(&objects_.api_2_8.audio, 0, processor);
+ if (FAILED(hr)) {
+ XELOGE("XAudio2Create failed with %.8X", hr);
+ assert_always();
+ return false;
+ }
+ return InitializeObjects(objects_.api_2_8);
+ } else {
+ hr = CoCreateInstance(__uuidof(api::XAudio2_7), NULL, CLSCTX_INPROC_SERVER,
+ IID_PPV_ARGS(&objects_.api_2_7.audio));
+ if (FAILED(hr)) {
+ XELOGE("CoCreateInstance for XAudio2 failed with %.8X", hr);
+ assert_always();
+ return false;
+ }
+ hr = objects_.api_2_7.audio->Initialize(0, processor);
+ if (FAILED(hr)) {
+ XELOGE("IXAudio2::Initialize failed with %.8X", hr);
+ assert_always();
+ return false;
+ }
+ return InitializeObjects(objects_.api_2_7);
}
+}
- hr = xaudio2_create(&audio_, 0, XAUDIO2_DEFAULT_PROCESSOR);
- if (FAILED(hr)) {
- XELOGE("XAudio2Create failed with %.8X", hr);
- assert_always();
- return false;
- }
+template <typename Objects>
+bool XAudio2AudioDriver::InitializeObjects(Objects& objects) {
+ HRESULT hr;
- XAUDIO2_DEBUG_CONFIGURATION config;
- config.TraceMask = XAUDIO2_LOG_ERRORS | XAUDIO2_LOG_WARNINGS;
+ api::XAUDIO2_DEBUG_CONFIGURATION config;
+ config.TraceMask = api::XE_XAUDIO2_LOG_ERRORS | api::XE_XAUDIO2_LOG_WARNINGS;
config.BreakMask = 0;
config.LogThreadID = FALSE;
config.LogTiming = TRUE;
config.LogFunctionName = TRUE;
config.LogFileline = TRUE;
- audio_->SetDebugConfiguration(&config);
+ objects.audio->SetDebugConfiguration(&config);
- hr = audio_->CreateMasteringVoice(&mastering_voice_);
+ hr = objects.audio->CreateMasteringVoice(&objects.mastering_voice);
if (FAILED(hr)) {
- XELOGE("CreateMasteringVoice failed with %.8X", hr);
+ XELOGE("IXAudio2::CreateMasteringVoice failed with %.8X", hr);
assert_always();
return false;
}
@@ -132,27 +153,38 @@ bool XAudio2AudioDriver::Initialize() {
waveformat.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
waveformat.Samples.wValidBitsPerSample = waveformat.Format.wBitsPerSample;
- waveformat.dwChannelMask = ChannelMasks[waveformat.Format.nChannels];
+ static const DWORD kChannelMasks[] = {
+ 0,
+ 0,
+ SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT,
+ 0,
+ 0,
+ 0,
+ SPEAKER_FRONT_LEFT | SPEAKER_FRONT_CENTER | SPEAKER_FRONT_RIGHT |
+ SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT,
+ 0,
+ };
+ waveformat.dwChannelMask = kChannelMasks[waveformat.Format.nChannels];
- hr = audio_->CreateSourceVoice(
- &pcm_voice_, &waveformat.Format,
- 0, // XAUDIO2_VOICE_NOSRC | XAUDIO2_VOICE_NOPITCH,
- XAUDIO2_MAX_FREQ_RATIO, voice_callback_);
+ hr = objects.audio->CreateSourceVoice(
+ &objects.pcm_voice, &waveformat.Format,
+ 0, // api::XE_XAUDIO2_VOICE_NOSRC | api::XE_XAUDIO2_VOICE_NOPITCH,
+ api::XE_XAUDIO2_MAX_FREQ_RATIO, voice_callback_);
if (FAILED(hr)) {
- XELOGE("CreateSourceVoice failed with %.8X", hr);
+ XELOGE("IXAudio2::CreateSourceVoice failed with %.8X", hr);
assert_always();
return false;
}
- hr = pcm_voice_->Start();
+ hr = objects.pcm_voice->Start();
if (FAILED(hr)) {
- XELOGE("Start failed with %.8X", hr);
+ XELOGE("IXAudio2SourceVoice::Start failed with %.8X", hr);
assert_always();
return false;
}
if (cvars::mute) {
- pcm_voice_->SetVolume(0.0f);
+ objects.pcm_voice->SetVolume(0.0f);
}
return true;
@@ -162,8 +194,13 @@ void XAudio2AudioDriver::SubmitFrame(uint32_t frame_ptr) {
// Process samples! They are big-endian floats.
HRESULT hr;
- XAUDIO2_VOICE_STATE state;
- pcm_voice_->GetState(&state, XAUDIO2_VOICE_NOSAMPLESPLAYED);
+ api::XAUDIO2_VOICE_STATE state;
+ if (api_minor_version_ >= 8) {
+ objects_.api_2_8.pcm_voice->GetState(&state,
+ api::XE_XAUDIO2_VOICE_NOSAMPLESPLAYED);
+ } else {
+ objects_.api_2_7.pcm_voice->GetState(&state);
+ }
assert_true(state.BuffersQueued < frame_count_);
auto input_frame = memory_->TranslateVirtual<float*>(frame_ptr);
@@ -178,17 +215,21 @@ void XAudio2AudioDriver::SubmitFrame(uint32_t frame_ptr) {
}
}
- XAUDIO2_BUFFER buffer;
+ api::XAUDIO2_BUFFER buffer;
buffer.Flags = 0;
buffer.pAudioData = reinterpret_cast<BYTE*>(output_frame);
buffer.AudioBytes = frame_size_;
buffer.PlayBegin = 0;
buffer.PlayLength = channel_samples_;
- buffer.LoopBegin = XAUDIO2_NO_LOOP_REGION;
+ buffer.LoopBegin = api::XE_XAUDIO2_NO_LOOP_REGION;
buffer.LoopLength = 0;
buffer.LoopCount = 0;
buffer.pContext = 0;
- hr = pcm_voice_->SubmitSourceBuffer(&buffer);
+ if (api_minor_version_ >= 8) {
+ hr = objects_.api_2_8.pcm_voice->SubmitSourceBuffer(&buffer);
+ } else {
+ hr = objects_.api_2_7.pcm_voice->SubmitSourceBuffer(&buffer);
+ }
if (FAILED(hr)) {
XELOGE("SubmitSourceBuffer failed with %.8X", hr);
assert_always();
@@ -199,26 +240,19 @@ void XAudio2AudioDriver::SubmitFrame(uint32_t frame_ptr) {
// Update playback ratio to our time scalar.
// This will keep audio in sync with the game clock.
- pcm_voice_->SetFrequencyRatio(
- static_cast<float>(xe::Clock::guest_time_scalar()));
+ float frequency_ratio = static_cast<float>(xe::Clock::guest_time_scalar());
+ if (api_minor_version_ >= 8) {
+ objects_.api_2_8.pcm_voice->SetFrequencyRatio(frequency_ratio);
+ } else {
+ objects_.api_2_7.pcm_voice->SetFrequencyRatio(frequency_ratio);
+ }
}
void XAudio2AudioDriver::Shutdown() {
- if (pcm_voice_) {
- pcm_voice_->Stop();
- pcm_voice_->DestroyVoice();
- pcm_voice_ = NULL;
- }
-
- if (mastering_voice_) {
- mastering_voice_->DestroyVoice();
- mastering_voice_ = NULL;
- }
-
- if (audio_) {
- audio_->StopEngine();
- audio_->Release();
- audio_ = nullptr;
+ if (api_minor_version_ >= 8) {
+ ShutdownObjects(objects_.api_2_8);
+ } else {
+ ShutdownObjects(objects_.api_2_7);
}
if (xaudio2_module_) {
@@ -232,6 +266,26 @@ void XAudio2AudioDriver::Shutdown() {
}
}
+template <typename Objects>
+void XAudio2AudioDriver::ShutdownObjects(Objects& objects) {
+ if (objects.pcm_voice) {
+ objects.pcm_voice->Stop();
+ objects.pcm_voice->DestroyVoice();
+ objects.pcm_voice = nullptr;
+ }
+
+ if (objects.mastering_voice) {
+ objects.mastering_voice->DestroyVoice();
+ objects.mastering_voice = nullptr;
+ }
+
+ if (objects.audio) {
+ objects.audio->StopEngine();
+ objects.audio->Release();
+ objects.audio = nullptr;
+ }
+}
+
} // namespace xaudio2
} // namespace apu
} // namespace xe
diff --git a/src/xenia/apu/xaudio2/xaudio2_audio_driver.h b/src/xenia/apu/xaudio2/xaudio2_audio_driver.h
index 32429d084..42d4b59a9 100644
--- a/src/xenia/apu/xaudio2/xaudio2_audio_driver.h
+++ b/src/xenia/apu/xaudio2/xaudio2_audio_driver.h
@@ -11,6 +11,7 @@
#define XENIA_APU_XAUDIO2_XAUDIO2_AUDIO_DRIVER_H_
#include "xenia/apu/audio_driver.h"
+#include "xenia/apu/xaudio2/xaudio2_api.h"
#include "xenia/base/threading.h"
struct IXAudio2;
@@ -31,16 +32,31 @@ class XAudio2AudioDriver : public AudioDriver {
void Shutdown();
private:
+ template <typename Objects>
+ bool InitializeObjects(Objects& objects);
+ template <typename Objects>
+ void ShutdownObjects(Objects& objects);
+
void* xaudio2_module_ = nullptr;
- IXAudio2* audio_ = nullptr;
- IXAudio2MasteringVoice* mastering_voice_ = nullptr;
- IXAudio2SourceVoice* pcm_voice_ = nullptr;
+ uint32_t api_minor_version_ = 7;
+ union {
+ struct {
+ api::IXAudio2_7* audio;
+ api::IXAudio2_7MasteringVoice* mastering_voice;
+ api::IXAudio2_7SourceVoice* pcm_voice;
+ } api_2_7;
+ struct {
+ api::IXAudio2_8* audio;
+ api::IXAudio2_8MasteringVoice* mastering_voice;
+ api::IXAudio2_8SourceVoice* pcm_voice;
+ } api_2_8;
+ } objects_ = {};
xe::threading::Semaphore* semaphore_ = nullptr;
class VoiceCallback;
VoiceCallback* voice_callback_ = nullptr;
- static const uint32_t frame_count_ = 64;
+ static const uint32_t frame_count_ = api::XE_XAUDIO2_MAX_QUEUED_BUFFERS;
static const uint32_t frame_channels_ = 6;
static const uint32_t channel_samples_ = 256;
static const uint32_t frame_samples_ = frame_channels_ * channel_samples_;
diff --git a/src/xenia/apu/xaudio2/xaudio2_audio_system.cc b/src/xenia/apu/xaudio2/xaudio2_audio_system.cc
index 37869a196..e9ddeb6ec 100644
--- a/src/xenia/apu/xaudio2/xaudio2_audio_system.cc
+++ b/src/xenia/apu/xaudio2/xaudio2_audio_system.cc
@@ -9,8 +9,6 @@
#include "xenia/apu/xaudio2/xaudio2_audio_system.h"
-#include <xaudio2.h>
-
#include "xenia/apu/apu_flags.h"
#include "xenia/apu/xaudio2/xaudio2_audio_driver.h"